ReactSVG Class Component Refactoring 1-初期Refactoring
テストが完了する前に作成したプロジェクトなので、再パッケージしながらコードを整理します.
コードは、実際に有効なレベルではなく、現在の機能のみを実行します.
実際には,コードを有効レベルに達させるとともに,後で修正するためにコードを再パッケージする.
現在の再構築の目的 stateを積極的に利用します.
注意事項 setState()は非同期(内部キューが存在し、キューをポーリングする方法で)であり、実行時に変数はすぐに変更されません.
->リアルタイムアニメーションに影響しますか?
意外なことに、文法は以下の通りです.は、イベントをオブジェクト内の関数として宣言し、使用するために参照します. 上記イベントでこのキーワードを使用するバインド方式は である.
変な、特にお勧めの方法ではないと思いますが、正式な書類でも使われています.
しかし、良い代替案が思いつかないので、今もこの方式を維持し続けています.
しかし、varはletに変更された.
以下は機動の再構築を確認する試みです.
このコードにエラーが発生しました.
エラーをよく確認すると、次のコードが表示されます.
(ReactSVGの値を変更すると、既存のSVGが削除され、新しいSVGがレンダリングされます)
今の問題は、
なぜこんなことが起こったのか理解してください.
this.setState(..) で行ないます.
この値を超えると、Stateを設定できるキューに移動します.
enqueueSetState(...)内部は以下のように伸びている.
EnqueueUpdate関数の内部は次のようになります.
この共有キューが保留中であることを確認する部門があります.
懸案がない場合は、updateのnextをすぐにさっきのキューに入れてください.
保留中の場合は、保留中のキューに立っています.
この過程自体は何も繰り返すことができなくて、重要なのはそのQの内容です.
上記リンクは、次のコードを示します.
回答として、以下の文章があります.
The function onDigit sets the state, which causes a rerender, which causes onDigit to fire because that’s the value you’re setting as onClick which causes the state to be set which causes a rerender, which causes onDigit to fire because that’s the value you’re… Etc
(翻訳)
onDigit機能は状態を設定することによって再レンダリングされ、onDigitはこの値がonClickに設定されているため実行されます.以下を繰り返します.
値を変更すると、下部のbuttonコンポーネントが再表示されます.したがって、inputDigitを再起動してbuttonコンポーネントのonClickを定義します.再び値をsetStateに変更すると、再表示されます.以下無限反復.
クエリの応答:があります.
要するに、
結局,以上のコードはクロスリサイクルである.素子はRealtSVGの形をしており、レンダリングを試みている. レンダリング中、svgはsetStateを介して事前投影された関数の内部でsvgを定義しようとした. 2回の試行でsetStateが呼び出されたため、renderは再び発生します. 以下無限反復 したがってsvgはstateではなくsetStatus()を呼び出すことはできません.
svgは単純な内部変数としてしか存在しない.
svgはstateの一員ではなく、setStateの最適化範囲を超えています.
エラーは発生しません.
しかし,以前に実現したすべての機能は機能せず,さらなる修正が必要である.
コードは、実際に有効なレベルではなく、現在の機能のみを実行します.
実際には,コードを有効レベルに達させるとともに,後で修正するためにコードを再パッケージする.
現在の再構築の目的
->リアルタイムアニメーションに影響しますか?
特別事項
意外なことに、文法は以下の通りです.
変な、特にお勧めの方法ではないと思いますが、正式な書類でも使われています.
svg.addEventListener('pointerdown', this.onPointerDown.bind(this));
//...중략
// Function called by the event listeners when user start pressing/touching
onPointerDown(event) {
this.isPointerDown = true; // We set the pointer as down
// We get the pointer position on click/touchdown so we can get the value once the user starts to drag
var pointerPosition = getPointFromEvent(event);
this.pointerOrigin.x = pointerPosition.x;
this.pointerOrigin.y = pointerPosition.y;
}
もちろん、これが正しいとは保証できません.しかし、良い代替案が思いつかないので、今もこの方式を維持し続けています.
しかし、varはletに変更された.
最初の再構築の試み
以下は機動の再構築を確認する試みです.
import kr_map from './kr.svg'
import React from 'react'
import { ReactSVG } from 'react-svg'
import gsap from 'gsap/all';
//이 함수는 class와 아무 관계 없이 독립적이므로 독립시킨다.
function getPointFromEvent (event) {
let point = {x:0, y:0};
// If event is triggered by a touch event, we get the position of the first finger
if (event.targetTouches) {
point.x = event.targetTouches[0].clientX;
point.y = event.targetTouches[0].clientY;
} else {
point.x = event.clientX;
point.y = event.clientY;
}
return point;
}
function animatingViewBox(target, x, y, width, height){
gsap.to(target, {
duration: .5,
attr: {viewBox: `${x} ${y} ${width} ${height}`},
ease: "power3.inOut"
});
}
export default class map extends React.Component {
constructor(props){
super(props);
this.state = {
current_viewbox : {
x:0,
y:0,
width:1200,
height:1080
},
new_viewbox : {
x:0,
y:0,
width:1200,
height:1080
},
pointerOrigin : {
x:0,
y:0
},
isPointerDown : false,
svg : null
}
}
// This function returns an object with X & Y values from the pointer event
onPointerUp() {
this.setState(prevState => ({
// The pointer is no longer considered as down
isPointerDown : false,
// We save the viewBox coordinates based on the last pointer offsets
current_viewbox : {
x : prevState.new_viewbox.x,
y : prevState.new_viewbox.y,
width: prevState.current_viewbox.width,
height: prevState.current_viewbox.height
}
}));
}
// Function called by the event listeners when user start pressing/touching
onPointerDown(event) {
let pointerPosition = getPointFromEvent(event);
this.setState({
isPointerDown : true, // We set the pointer as down
pointerOrigin : {
// We get the pointer position on click/touchdown so we can get the value once the user starts to drag
x : pointerPosition.x,
y : pointerPosition.y
}
});
}
// Function called by the event listeners when user start moving/dragging
onPointerMove (event) {
// Only run this function if the pointer is down
if (!this.state.isPointerDown) {
return;
}
// This prevent user to do a selection on the page
event.preventDefault();
// Get the pointer position
let pointerPosition = getPointFromEvent(event);
// We calculate the distance between the pointer origin and the current position
// The viewBox x & y values must be calculated from the original values and the distances
this.setState(prevState=> ({
new_viewbox : {
x: prevState.current_viewbox.x - (pointerPosition.x - prevState.pointerOrigin.x),
y: prevState.current_viewbox.y - (pointerPosition.y - prevState.pointerOrigin.y)
}
}));
}
onZoom(event){
if(event.deltaY > 0){
this.setState(prevState => ({
current_viewbox : {
width : prevState.current_viewbox.width / 9,
height: prevState.current_viewbox.height / 9
}
}))
}
else if(event.deltaY < 0){
this.setState(prevState => ({
current_viewbox : {
width : prevState.current_viewbox.width / 1.1,
height: prevState.current_viewbox.height / 1.1
}
}))
}
}
onClick(event){
if(event.target.getAttribute('name'))
{
let rect = event.target.getBoundingClientRect();
this.setState({
current_viewbox : {
x : rect.top,
y : rect.bottom,
width : rect.width,
height: rect.height
}
},
() => {
animatingViewBox(this.svg, this.current_viewbox.x, this.current_viewbox.y, this.current_viewbox.width, this.current_viewbox.height);
})
}
}
render(){
return <ReactSVG
beforeInjection = {(inject_svg) => {
inject_svg.setAttribute('width',`${this.state.current_viewbox.width}`);
inject_svg.setAttribute('height',`${this.state.current_viewbox.height}`);
inject_svg.setAttribute('viewBox',`${this.state.current_viewbox.x} ${this.state.current_viewbox.y} ${this.state.current_viewbox.height} ${this.state.current_viewbox.height}`);
if (window.PointerEvent) {
inject_svg.addEventListener('pointerdown', this.onPointerDown.bind(this)); // Pointer is pressed
inject_svg.addEventListener('pointerup', this.onPointerUp.bind(this)); // Releasing the pointer
inject_svg.addEventListener('pointerleave', this.onPointerUp.bind(this)); // Pointer gets out of the SVG area
inject_svg.addEventListener('pointermove', this.onPointerMove.bind(this)); // Pointer is moving
inject_svg.addEventListener('wheel', this.onZoom.bind(this));
inject_svg.addEventListener('click', this.onClick.bind(this));
} else {
// Add all mouse events listeners fallback
inject_svg.addEventListener('mousedown', this.onPointerDown.bind(this)); // Pressing the mouse
inject_svg.addEventListener('mouseup', this.onPointerUp.bind(this)); // Releasing the mouse
inject_svg.addEventListener('mouseleave', this.onPointerUp.bind(this)); // Mouse gets out of the SVG area
inject_svg.addEventListener('mousemove', this.onPointerMove.bind(this)); // Mouse is moving
// Add all touch events listeners fallback
inject_svg.addEventListener('touchstart', this.onPointerDown.bind(this)); // Finger is touching the screen
inject_svg.addEventListener('touchend', this.onPointerUp.bind(this)); // Finger is no longer touching the screen
inject_svg.addEventListener('touchmove', this.onPointerMove.bind(this)); // Finger is moving
}
//this.setState({svg : inject_svg})
}
}
afterInjection = {(error, svg) => {
if (error) {
console.error(error);
return;
}
svg.classList.add('region');
}
}
src = {kr_map}
></ReactSVG>
}
}
現在prevStateの操作やsetState完了時のコールバックのthis処理はまだあいまいで、エラーが発生しやすいコードです.しかし、予想とは異なるエラーが発生したため、記録しておく.このコードにエラーが発生しました.
162 | render(){
163 | return <ReactSVG
164 | beforeInjection = {(inject_svg) => {
> 165 | this.setState({svg : inject_svg});
エラーの内容は次のとおりです.Error: Maximum update depth exceeded.
This can happen when a component repeatedly calls setState inside componentWillUpdate or componentDidUpdate.
React limits the number of nested updates to prevent infinite loops.
上記のコードでは、setState
のプロセスにおいて、componentWillUpdate
/24579142コードが繰り返し呼び出され、無限回帰する.エラーをよく確認すると、次のコードが表示されます.
//ReactSVG.componentDidUpdate
//.../Projects/compiled/ReactSVG.js:114
112 | componentDidUpdate(prevProps) {
113 | if (shallowDiffers(prevProps, this.props)) {
> 114 | this.setState(() => this.initialState, () => {
115 | ^ this.removeSVG();
116 | this.renderSVG();
117 | });
ReactSVGに割り当てられているのはもう1つReactComponentとして管理されています.(ReactSVGの値を変更すると、既存のSVGが削除され、新しいSVGがレンダリングされます)
今の問題は、
this.setState({svg : inject_svg});
部門として、実際に削除しても無限の回帰問題は発生しません.なぜこんなことが起こったのか理解してください.
this.setState(..) で行ないます.
Component.prototype.setState = function (partialState, callback) {
if (!(typeof partialState === 'object' || typeof partialState === 'function' || partialState == null)) {
{
throw Error( "setState(...): takes an object of state variables to update or a function which returns an object of state variables." );
}
}
this.updater.enqueueSetState(this, partialState, callback, 'setState');
};
設定するステータスがobject、function、nullでない場合はErrorです.この値を超えると、Stateを設定できるキューに移動します.
enqueueSetState(...)内部は以下のように伸びている.
var classComponentUpdater = {
isMounted: isMounted,
//여기서 inst는 this이다.
enqueueSetState: function (inst, payload, callback) {
var fiber = get(inst);
var eventTime = requestEventTime();
var lane = requestUpdateLane(fiber);
var update = createUpdate(eventTime, lane);
update.payload = payload;
if (callback !== undefined && callback !== null) {
{
warnOnInvalidCallback(callback, 'setState');
}
update.callback = callback;
}
enqueueUpdate(fiber, update);
scheduleUpdateOnFiber(fiber, lane, eventTime);
},
//후략
コールバックなどを設定し、updateを作成してenqueueUpdateを行います.EnqueueUpdate関数の内部は次のようになります.
function enqueueUpdate(fiber, update) {
var updateQueue = fiber.updateQueue;
if (updateQueue === null) {
// Only occurs if the fiber has been unmounted.
return;
}
var sharedQueue = updateQueue.shared;
var pending = sharedQueue.pending;
if (pending === null) {
// This is the first update. Create a circular list.
update.next = update;
} else {
update.next = pending.next;
pending.next = update;
}
sharedQueue.pending = update;
{
if (currentlyProcessingQueue === sharedQueue && !didWarnUpdateInsideUpdate) {
error('An update (setState, replaceState, or forceUpdate) was scheduled ' + 'from inside an update function. Update functions should be pure, ' + 'with zero side-effects. Consider using componentDidUpdate or a ' + 'callback.');
didWarnUpdateInsideUpdate = true;
}
}
}
updateQueueには共有部分が含まれています.この共有キューが保留中であることを確認する部門があります.
懸案がない場合は、updateのnextをすぐにさっきのキューに入れてください.
保留中の場合は、保留中のキューに立っています.
この過程自体は何も繰り返すことができなくて、重要なのはそのQの内容です.
上記リンクは、次のコードを示します.
inputDigit(digit){
this.setState({
displayValue: String(digit)
})
<button type="button" onClick={this.inputDigit(0)}>0</button>;
上のコードはさっきと同じ無限回帰エラーを生成します.回答として、以下の文章があります.
The function onDigit sets the state, which causes a rerender, which causes onDigit to fire because that’s the value you’re setting as onClick which causes the state to be set which causes a rerender, which causes onDigit to fire because that’s the value you’re… Etc
(翻訳)
onDigit機能は状態を設定することによって再レンダリングされ、onDigitはこの値がonClickに設定されているため実行されます.以下を繰り返します.
値を変更すると、下部のbuttonコンポーネントが再表示されます.したがって、inputDigitを再起動してbuttonコンポーネントのonClickを定義します.再び値をsetStateに変更すると、再表示されます.以下無限反復.
クエリの応答:があります.
要するに、
componentDidUpdate
ではrender
と呼ばないでください.結局,以上のコードはクロスリサイクルである.
svgは単純な内部変数としてしか存在しない.
二次改造
import kr_map from './kr.svg'
import React from 'react'
import { ReactSVG } from 'react-svg'
import gsap from 'gsap/all';
//이 함수는 class와 아무 관계 없이 독립적이므로 독립시킨다.
function getPointFromEvent (event) {
let point = {x:0, y:0};
// If event is triggered by a touch event, we get the position of the first finger
if (event.targetTouches) {
point.x = event.targetTouches[0].clientX;
point.y = event.targetTouches[0].clientY;
} else {
point.x = event.clientX;
point.y = event.clientY;
}
return point;
}
function animatingViewBox(target, x, y, width, height){
gsap.to(target, {
duration: .5,
attr: {viewBox: `${x} ${y} ${width} ${height}`},
ease: "power3.inOut"
});
}
export default class map extends React.Component {
constructor(props){
super(props);
this.svg = null
this.state = {
current_viewbox : {
x:0,
y:0,
width:1200,
height:1080
},
new_viewbox : {
x:0,
y:0,
width:1200,
height:1080
},
pointerOrigin : {
x:0,
y:0
},
isPointerDown : false,
}
}
// This function returns an object with X & Y values from the pointer event
onPointerUp() {
this.setState(prevState => ({
// The pointer is no longer considered as down
isPointerDown : false,
// We save the viewBox coordinates based on the last pointer offsets
current_viewbox : {
x : prevState.new_viewbox.x,
y : prevState.new_viewbox.y,
width: prevState.current_viewbox.width,
height: prevState.current_viewbox.height
}
}));
}
// Function called by the event listeners when user start pressing/touching
onPointerDown(event) {
let pointerPosition = getPointFromEvent(event);
this.setState({
isPointerDown : true, // We set the pointer as down
pointerOrigin : {
// We get the pointer position on click/touchdown so we can get the value once the user starts to drag
x : pointerPosition.x,
y : pointerPosition.y
}
});
}
// Function called by the event listeners when user start moving/dragging
onPointerMove (event) {
// Only run this function if the pointer is down
if (!this.state.isPointerDown) {
return;
}
// This prevent user to do a selection on the page
event.preventDefault();
// Get the pointer position
let pointerPosition = getPointFromEvent(event);
// We calculate the distance between the pointer origin and the current position
// The viewBox x & y values must be calculated from the original values and the distances
this.setState(prevState=> ({
new_viewbox : {
x: prevState.current_viewbox.x - (pointerPosition.x - prevState.pointerOrigin.x),
y: prevState.current_viewbox.y - (pointerPosition.y - prevState.pointerOrigin.y)
}
}));
}
onZoom(event){
if(event.deltaY > 0){
this.setState(prevState => ({
current_viewbox : {
width : prevState.current_viewbox.width / 9,
height: prevState.current_viewbox.height / 9
}
}))
}
else if(event.deltaY < 0){
this.setState(prevState => ({
current_viewbox : {
width : prevState.current_viewbox.width / 1.1,
height: prevState.current_viewbox.height / 1.1
}
}))
}
}
onClick(event){
if(event.target.getAttribute('name'))
{
let rect = event.target.getBoundingClientRect();
this.setState({
current_viewbox : {
x : rect.top,
y : rect.bottom,
width : rect.width,
height: rect.height
}
},
() => {
animatingViewBox(this.svg, this.current_viewbox.x, this.current_viewbox.y, this.current_viewbox.width, this.current_viewbox.height);
})
}
}
render(){
return <ReactSVG
beforeInjection = {(inject_svg) => {
inject_svg.setAttribute('width',`${this.state.current_viewbox.width}`);
inject_svg.setAttribute('height',`${this.state.current_viewbox.height}`);
inject_svg.setAttribute('viewBox',`${this.state.current_viewbox.x} ${this.state.current_viewbox.y} ${this.state.current_viewbox.height} ${this.state.current_viewbox.height}`);
if (window.PointerEvent) {
inject_svg.addEventListener('pointerdown', this.onPointerDown.bind(this)); // Pointer is pressed
inject_svg.addEventListener('pointerup', this.onPointerUp.bind(this)); // Releasing the pointer
inject_svg.addEventListener('pointerleave', this.onPointerUp.bind(this)); // Pointer gets out of the SVG area
inject_svg.addEventListener('pointermove', this.onPointerMove.bind(this)); // Pointer is moving
inject_svg.addEventListener('wheel', this.onZoom.bind(this));
inject_svg.addEventListener('click', this.onClick.bind(this));
} else {
// Add all mouse events listeners fallback
inject_svg.addEventListener('mousedown', this.onPointerDown.bind(this)); // Pressing the mouse
inject_svg.addEventListener('mouseup', this.onPointerUp.bind(this)); // Releasing the mouse
inject_svg.addEventListener('mouseleave', this.onPointerUp.bind(this)); // Mouse gets out of the SVG area
inject_svg.addEventListener('mousemove', this.onPointerMove.bind(this)); // Mouse is moving
// Add all touch events listeners fallback
inject_svg.addEventListener('touchstart', this.onPointerDown.bind(this)); // Finger is touching the screen
inject_svg.addEventListener('touchend', this.onPointerUp.bind(this)); // Finger is no longer touching the screen
inject_svg.addEventListener('touchmove', this.onPointerMove.bind(this)); // Finger is moving
}
this.svg = inject_svg
}
}
afterInjection = {(error, inject_svg) => {
if (error) {
console.error(error);
return;
}
inject_svg.classList.add('region');
}
}
src = {kr_map}
></ReactSVG>
}
}
svgを独立させる.svgはstateの一員ではなく、setStateの最適化範囲を超えています.
エラーは発生しません.
しかし,以前に実現したすべての機能は機能せず,さらなる修正が必要である.
Reference
この問題について(ReactSVG Class Component Refactoring 1-初期Refactoring), 我々は、より多くの情報をここで見つけました https://velog.io/@fgprjs/ReactSVG-Class-Component-리팩토링하기-1-Recursive-Error편テキストは自由に共有またはコピーできます。ただし、このドキュメントのURLは参考URLとして残しておいてください。
Collection and Share based on the CC Protocol