React hooks 函数有旧版本的状态变量
React hooks functions have old version of a state var
我尝试使用 Hooks 为自己制作一个很好的动画示例,但我偶然发现了一个问题,即我的函数没有状态变量的更新版本并继续使用第一个版本。
在下面的代码片段中,我举了一个例子,一旦你点击一个条,它应该以正方形移动。首先向东,然后向南,然后向西,然后向北,然后再次向东等等。但是它永远不会向南,因为即使它的方向从 north
更新为 east
(由栏上的文本指示或再次点击),函数仍然认为方向是北。
const Example = (props) => {
const [ direction, setDirection ] = React.useState('North');
console.log("Rendering Example: ", direction);
const isAnimating = React.useRef(false)
const blockRef = React.useRef(null);
// Animate on click.
const onClick = () => {
if (!isAnimating.current) {
decideDirection();
isAnimating.current = true
} else {
console.log("Already animating. Going: ", direction);
}
};
const decideDirection = () => {
console.log("Current direction: ", direction);
if (direction === 'North') {
moveEast();
} else if (direction === 'East') {
moveSouth();
} else if (direction === 'South') {
moveWest();
} else if (direction === 'West') {
moveNorth();
}
};
const move = (toX, toY, duration, onComplete) => {
Velocity(blockRef.current, {
translateX: toX,
translateY: toY,
complete: () => {
onComplete();
}
},
{
duration: duration
});
}
const moveNorth = () => {
setDirection('North');
console.log('Moving N: ', direction);
move(0, 0, 500, () => {
decideDirection();
})
}
const moveEast = () => {
setDirection('East');
console.log('Moving E: ', direction);
move(500, 0, 2500, () => {
decideDirection();
})
};
const moveSouth = () => {
setDirection('South');
console.log('Moving S: ', direction);
move(500, 18, 500, () => {
decideDirection();
})
}
const moveWest = () => {
setDirection('West');
console.log('Moving W: ', direction);
move(0, 18, 2500, () => {
decideDirection();
})
}
return(
<div>
<div id='block' onClick={onClick} ref={blockRef} style={{ width: '100px', height: '18px', backgroundColor: 'red', textAlign: 'center'}}>{direction}</div>
</div>
);
};
ReactDOM.render(<div><Example/></div>, document.getElementById('root'));
<script src="https://cdnjs.cloudflare.com/ajax/libs/velocity/1.2.2/velocity.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/lodash@4.17.11/lodash.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.8.3/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.8.3/umd/react-dom.production.min.js"></script>
<div id='root' style='width: 100%; height: 100%'>
</div>
我觉得这很奇怪,因为 none 这些函数被记忆了,所以它们应该重新创建每个渲染并因此具有新值。即使我确实添加了 useCallback
之类的东西并为每个函数提供 direction
它仍然无法正常工作。为什么函数不知道状态变量的更新版本?
问题是您的 move
功能已在 decideDirection
的初始版本上关闭。您的 div 已重新呈现,但动画继续引用函数的初始版本。解决此问题的一种方法是使用 ref 指向 decideDirection
函数(我在下面的代码片段中展示了这种方法)。
这似乎有点脆弱,因为重新渲染的时间与动画的时间相比,很难推断这是否可靠。一种更稳健的方法是通过状态更改更明确地链接移动,而不是在每个动画完成时直接调用 decideDirection
,而是设置将触发效果以启动下一个动画的状态。
const Example = (props) => {
const [ direction, setDirection ] = React.useState('North');
console.log("Rendering Example: ", direction);
const isAnimating = React.useRef(false)
const blockRef = React.useRef(null);
const decideDirection = () => {
console.log("Current direction: ", direction);
if (direction === 'North') {
moveEast();
} else if (direction === 'East') {
moveSouth();
} else if (direction === 'South') {
moveWest();
} else if (direction === 'West') {
moveNorth();
}
};
const decideDirectionRef = React.useRef(decideDirection);
React.useEffect(()=>{
decideDirectionRef.current = decideDirection;
});
// Animate on click.
const onClick = () => {
if (!isAnimating.current) {
decideDirectionRef.current();
isAnimating.current = true
} else {
console.log("Already animating. Going: ", direction);
}
};
const move = (toX, toY, duration, onComplete) => {
Velocity(blockRef.current, {
translateX: toX,
translateY: toY,
complete: () => {
onComplete();
}
},
{
duration: duration
});
}
const moveNorth = () => {
setDirection('North');
console.log('Moving N: ', direction);
move(0, 0, 500, () => {
decideDirectionRef.current();
})
}
const moveEast = () => {
setDirection('East');
console.log('Moving E: ', direction);
move(500, 0, 2500, () => {
decideDirectionRef.current();
})
};
const moveSouth = () => {
setDirection('South');
console.log('Moving S: ', direction);
move(500, 18, 500, () => {
decideDirectionRef.current();
})
}
const moveWest = () => {
setDirection('West');
console.log('Moving W: ', direction);
move(0, 18, 2500, () => {
decideDirectionRef.current();
})
}
return(
<div>
<div id='block' onClick={onClick} ref={blockRef} style={{ width: '100px', height: '18px', backgroundColor: 'red', textAlign: 'center'}}>{direction}</div>
</div>
);
};
ReactDOM.render(<div><Example/></div>, document.getElementById('root'));
<script src="https://cdnjs.cloudflare.com/ajax/libs/velocity/1.2.2/velocity.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/lodash@4.17.11/lodash.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.8.3/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.8.3/umd/react-dom.production.min.js"></script>
<div id='root' style='width: 100%; height: 100%'>
</div>
你的值是捕获的,看看
我尝试使用 Hooks 为自己制作一个很好的动画示例,但我偶然发现了一个问题,即我的函数没有状态变量的更新版本并继续使用第一个版本。
在下面的代码片段中,我举了一个例子,一旦你点击一个条,它应该以正方形移动。首先向东,然后向南,然后向西,然后向北,然后再次向东等等。但是它永远不会向南,因为即使它的方向从 north
更新为 east
(由栏上的文本指示或再次点击),函数仍然认为方向是北。
const Example = (props) => {
const [ direction, setDirection ] = React.useState('North');
console.log("Rendering Example: ", direction);
const isAnimating = React.useRef(false)
const blockRef = React.useRef(null);
// Animate on click.
const onClick = () => {
if (!isAnimating.current) {
decideDirection();
isAnimating.current = true
} else {
console.log("Already animating. Going: ", direction);
}
};
const decideDirection = () => {
console.log("Current direction: ", direction);
if (direction === 'North') {
moveEast();
} else if (direction === 'East') {
moveSouth();
} else if (direction === 'South') {
moveWest();
} else if (direction === 'West') {
moveNorth();
}
};
const move = (toX, toY, duration, onComplete) => {
Velocity(blockRef.current, {
translateX: toX,
translateY: toY,
complete: () => {
onComplete();
}
},
{
duration: duration
});
}
const moveNorth = () => {
setDirection('North');
console.log('Moving N: ', direction);
move(0, 0, 500, () => {
decideDirection();
})
}
const moveEast = () => {
setDirection('East');
console.log('Moving E: ', direction);
move(500, 0, 2500, () => {
decideDirection();
})
};
const moveSouth = () => {
setDirection('South');
console.log('Moving S: ', direction);
move(500, 18, 500, () => {
decideDirection();
})
}
const moveWest = () => {
setDirection('West');
console.log('Moving W: ', direction);
move(0, 18, 2500, () => {
decideDirection();
})
}
return(
<div>
<div id='block' onClick={onClick} ref={blockRef} style={{ width: '100px', height: '18px', backgroundColor: 'red', textAlign: 'center'}}>{direction}</div>
</div>
);
};
ReactDOM.render(<div><Example/></div>, document.getElementById('root'));
<script src="https://cdnjs.cloudflare.com/ajax/libs/velocity/1.2.2/velocity.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/lodash@4.17.11/lodash.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.8.3/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.8.3/umd/react-dom.production.min.js"></script>
<div id='root' style='width: 100%; height: 100%'>
</div>
我觉得这很奇怪,因为 none 这些函数被记忆了,所以它们应该重新创建每个渲染并因此具有新值。即使我确实添加了 useCallback
之类的东西并为每个函数提供 direction
它仍然无法正常工作。为什么函数不知道状态变量的更新版本?
问题是您的 move
功能已在 decideDirection
的初始版本上关闭。您的 div 已重新呈现,但动画继续引用函数的初始版本。解决此问题的一种方法是使用 ref 指向 decideDirection
函数(我在下面的代码片段中展示了这种方法)。
这似乎有点脆弱,因为重新渲染的时间与动画的时间相比,很难推断这是否可靠。一种更稳健的方法是通过状态更改更明确地链接移动,而不是在每个动画完成时直接调用 decideDirection
,而是设置将触发效果以启动下一个动画的状态。
const Example = (props) => {
const [ direction, setDirection ] = React.useState('North');
console.log("Rendering Example: ", direction);
const isAnimating = React.useRef(false)
const blockRef = React.useRef(null);
const decideDirection = () => {
console.log("Current direction: ", direction);
if (direction === 'North') {
moveEast();
} else if (direction === 'East') {
moveSouth();
} else if (direction === 'South') {
moveWest();
} else if (direction === 'West') {
moveNorth();
}
};
const decideDirectionRef = React.useRef(decideDirection);
React.useEffect(()=>{
decideDirectionRef.current = decideDirection;
});
// Animate on click.
const onClick = () => {
if (!isAnimating.current) {
decideDirectionRef.current();
isAnimating.current = true
} else {
console.log("Already animating. Going: ", direction);
}
};
const move = (toX, toY, duration, onComplete) => {
Velocity(blockRef.current, {
translateX: toX,
translateY: toY,
complete: () => {
onComplete();
}
},
{
duration: duration
});
}
const moveNorth = () => {
setDirection('North');
console.log('Moving N: ', direction);
move(0, 0, 500, () => {
decideDirectionRef.current();
})
}
const moveEast = () => {
setDirection('East');
console.log('Moving E: ', direction);
move(500, 0, 2500, () => {
decideDirectionRef.current();
})
};
const moveSouth = () => {
setDirection('South');
console.log('Moving S: ', direction);
move(500, 18, 500, () => {
decideDirectionRef.current();
})
}
const moveWest = () => {
setDirection('West');
console.log('Moving W: ', direction);
move(0, 18, 2500, () => {
decideDirectionRef.current();
})
}
return(
<div>
<div id='block' onClick={onClick} ref={blockRef} style={{ width: '100px', height: '18px', backgroundColor: 'red', textAlign: 'center'}}>{direction}</div>
</div>
);
};
ReactDOM.render(<div><Example/></div>, document.getElementById('root'));
<script src="https://cdnjs.cloudflare.com/ajax/libs/velocity/1.2.2/velocity.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/lodash@4.17.11/lodash.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.8.3/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.8.3/umd/react-dom.production.min.js"></script>
<div id='root' style='width: 100%; height: 100%'>
</div>
你的值是捕获的,看看