我在我的React应用程序中创建了一个自定义钩子,它将一些函数传递给挂钩所连接的Component。这些函数用于根据钩子是否认为自己是mounted来更新媒体。
但是,我遇到了一个问题,当我从钩子返回一个函数(play),然后调用该函数时,它会根据钩子的初始状态更新数据(尽管状态已经更新了很多次)。
const usePlayer = () => {
const audioRef = useRef(__GLOBAL_AUDIO);
const [state, setState] = useReducer((state, newState) => ({...state, ...newState}), {
mounted: false,
playing: false,
});
const play = () => ensureMounted(() => {
audioRef.current.play();
});
const ensureMounted = callback => {
// This if statement will never run as this function does not
// seem to get any state other than the initially supplied one
if(state.mounted){
callback();
return;
}
// ensureMounted will always skip to this portion of the function
// despite what the current state is
audioRef.current.src = cdn("/link/to/new/src.mp3");
onLoad(callback);
};
useEffect(() => {
// This function is tied to redux, but is irrelevant. Just know
// that mounted does change from true to false and vice-versa - often.
const _mounted = checkToSeeIfMounted();
setState({ mounted: _mounted });
}, deps);
return {
playing: state.playing,
play: play,
};
};显然,这段代码已经非常简化了,但我希望它仍然能传达我所面临的问题。我有一种预感,问题在于React可能是在传承一个旧的(?)引用每个重呈现上的函数,但我不确定,官方文档对于“高级”函数非常模糊,我不知道如何使用它们,或者它们是否有用。
所有的帮助都很感激,干杯。
发布于 2021-08-31 12:00:59
您的代码似乎像预期的那样工作。引用不存在明显的问题。(编辑:至少在你最初的问题的代码中)。
请记住,自定义hook在使用它的组件的“同一级别”工作。换句话说,就像您在组件中直接使用自定义钩子的useState一样。使您的自定义钩子‘重新呈现’的操作也将导致您的组件重新呈现。
请记住,状态更改通常是异步进行的。这有时会导致令人困惑的行为。
当您的代码第一次运行时,两个useEffect挂钩都按顺序运行。第一个useEffect将只运行一次,因为它没有依赖项(它中的hook.example引用始终是第一个引用(最终是旧的),但是它只运行一次,所以您不会遇到任何问题,除非您在回调中执行其他一些事情(比如添加事件侦听器或执行一些异步捕获),或者在useEffect运行之前调用hook.example (从而更改原始状态)。
在调用hook.example之后,继续记录数据的当前值(false),然后请求状态更新(异步),继续执行代码,并触发第二个useEffect,记录当前值(false)。最后,状态更新,您的自定义钩子‘重新呈现’导致您的组件也重新呈现。现在,第一次useEffect不触发,第二次useEffect触发是因为hook.data更改了。最后,记录新的数据值(true)。
当通过单击按钮调用hook.example时,没有问题,因为hook.example总是在那里保存最新的引用。
还请注意,良好实践要求您的第一个useEffect钩子将hook作为依赖项(然后应该在useEffect回调中执行最终的筛选逻辑),在您的示例中,向依赖项数组添加hook将导致无限循环。也许您的实际代码是不同的,但是知道这一点是很好的。
如果您需要更多的澄清,不要犹豫的评论。
const useHook = () => {
const [data, setData] = React.useState(false);
const example = () =>
preCallCheck(() => {
setData(!data);
});
const preCallCheck = (callback) => {
console.log('preCallCheck()\t', data);
callback();
};
return {
data: data,
example: example
};
};
const Parent = () => {
const hook = useHook();
React.useEffect(() => {
hook.example();
}, []);
React.useEffect(() => {
console.log('useEffect()\t', hook.data);
}, [hook.data]);
return (
<div>
<h1>{'Hello, World!'}</h1>
<button type={'button'} onClick={() => hook.example()}>{'Click Me'}</button>
</div>
);
};
ReactDOM.render(<Parent />, document.getElementById('root'));<script src="https://cdnjs.cloudflare.com/ajax/libs/react/17.0.1/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/17.0.1/umd/react-dom.production.min.js"></script>
<div id="root"></div>
https://stackoverflow.com/questions/68997679
复制相似问题