React 不更新 State (React-Hooks)
React does not update State (React-Hooks)
我有以下设置:
const templates = [
'dot',
'line',
'circle',
'square',
];
const [currentTemplate, setCurrentTemplate] = useState(0);
const changeTemplate = (forward = true) => {
let index = currentTemplate;
index = forward ? index + 1 : index - 1;
if (index > templates.length - 1) {
index = 0;
} else if (index < 0) {
index = templates.length - 1;
}
setCurrentTemplate(index);
};
useEffect(() => {
console.log(`Current Template is: ${templates[currentTemplate]}`);
}, [currentTemplate]);
useKeypress('ArrowLeft', () => {
changeTemplate(false);
});
useKeypress('ArrowRight', () => {
changeTemplate(true);
});
这个是useKeypress-Hook的用处:
import { useEffect } from 'react';
/**
* useKeyPress
* @param {string} key - the name of the key to respond to, compared against event.key
* @param {function} action - the action to perform on key press
*/
export default function useKeypress(key: string, action: () => void) {
useEffect(() => {
function onKeyup(e: KeyboardEvent) {
if (e.key === key) action();
}
window.addEventListener('keyup', onKeyup);
return () => window.removeEventListener('keyup', onKeyup);
}, []);
}
每当我按下向左或向右箭头键时,该功能就会被触发。但是 currentTemplate
变量没有改变。它始终保持在 0。 useEffect
仅在我从左向右或以其他方式切换键时触发。单击同一个键两次,不会再次触发 useEffect
,但它应该!从右向左变化时,输出为Current Template is: square
,从左向右变化时,输出为Current Template is: line
。但这永远不会改变。 currentTemplate
的值始终保持 0
.
我错过了什么?
问题出在 useKeypress
挂钩内的 useEffect
。
export default function useKeypress(key: string, action: () => void) {
useEffect(() => {
function onKeyup(e: KeyboardEvent) {
if (e.key === key) action();
}
window.addEventListener('keyup', onKeyup);
return () => window.removeEventListener('keyup', onKeyup);
}, []); // problem
}
该挂钩是单个 useEffect
,没有依赖项。意思是,它只会触发一次。这是您传递给此挂钩的 action
的问题。每当组件中的 changeTemplate
-action 发生变化时(即在重新渲染时),对 this 的引用不会在 useKeypress
-hook 内部更新。
要解决这个问题,需要将action
添加到useEffect
的依赖数组中:
export default function useKeypress(key: string, action: () => void) {
useEffect(() => {
function onKeyup(e: KeyboardEvent) {
if (e.key === key) action();
}
window.addEventListener('keyup', onKeyup);
return () => window.removeEventListener('keyup', onKeyup);
}, [action]); // whenever `action` changes, the effect will be updated
}
这是为了确保只要给定的 action
发生变化,效果就会更新。进行此更改后,事情应该会按预期进行。
正在优化:
此时将为每个渲染重新生成 useEffect
,因为将为每个渲染实例化 changeTemplate
函数。为避免这种情况,您可以用 useCallback
包裹 changeTemplate
,以便记忆函数:
const changeTemplate = useCallback(
(forward = true) => {
let index = currentTemplate;
index = forward ? index + 1 : index - 1;
if (index > templates.length - 1) {
index = 0;
} else if (index < 0) {
index = templates.length - 1;
}
setCurrentTemplate(index);
},
// Regenerate the callback whenever `currentTemplate` or `setCurrentTemplate` changes
[currentTemplate, setCurrentTemplate]
);
我有以下设置:
const templates = [
'dot',
'line',
'circle',
'square',
];
const [currentTemplate, setCurrentTemplate] = useState(0);
const changeTemplate = (forward = true) => {
let index = currentTemplate;
index = forward ? index + 1 : index - 1;
if (index > templates.length - 1) {
index = 0;
} else if (index < 0) {
index = templates.length - 1;
}
setCurrentTemplate(index);
};
useEffect(() => {
console.log(`Current Template is: ${templates[currentTemplate]}`);
}, [currentTemplate]);
useKeypress('ArrowLeft', () => {
changeTemplate(false);
});
useKeypress('ArrowRight', () => {
changeTemplate(true);
});
这个是useKeypress-Hook的用处:
import { useEffect } from 'react';
/**
* useKeyPress
* @param {string} key - the name of the key to respond to, compared against event.key
* @param {function} action - the action to perform on key press
*/
export default function useKeypress(key: string, action: () => void) {
useEffect(() => {
function onKeyup(e: KeyboardEvent) {
if (e.key === key) action();
}
window.addEventListener('keyup', onKeyup);
return () => window.removeEventListener('keyup', onKeyup);
}, []);
}
每当我按下向左或向右箭头键时,该功能就会被触发。但是 currentTemplate
变量没有改变。它始终保持在 0。 useEffect
仅在我从左向右或以其他方式切换键时触发。单击同一个键两次,不会再次触发 useEffect
,但它应该!从右向左变化时,输出为Current Template is: square
,从左向右变化时,输出为Current Template is: line
。但这永远不会改变。 currentTemplate
的值始终保持 0
.
我错过了什么?
问题出在 useKeypress
挂钩内的 useEffect
。
export default function useKeypress(key: string, action: () => void) {
useEffect(() => {
function onKeyup(e: KeyboardEvent) {
if (e.key === key) action();
}
window.addEventListener('keyup', onKeyup);
return () => window.removeEventListener('keyup', onKeyup);
}, []); // problem
}
该挂钩是单个 useEffect
,没有依赖项。意思是,它只会触发一次。这是您传递给此挂钩的 action
的问题。每当组件中的 changeTemplate
-action 发生变化时(即在重新渲染时),对 this 的引用不会在 useKeypress
-hook 内部更新。
要解决这个问题,需要将action
添加到useEffect
的依赖数组中:
export default function useKeypress(key: string, action: () => void) {
useEffect(() => {
function onKeyup(e: KeyboardEvent) {
if (e.key === key) action();
}
window.addEventListener('keyup', onKeyup);
return () => window.removeEventListener('keyup', onKeyup);
}, [action]); // whenever `action` changes, the effect will be updated
}
这是为了确保只要给定的 action
发生变化,效果就会更新。进行此更改后,事情应该会按预期进行。
正在优化:
此时将为每个渲染重新生成 useEffect
,因为将为每个渲染实例化 changeTemplate
函数。为避免这种情况,您可以用 useCallback
包裹 changeTemplate
,以便记忆函数:
const changeTemplate = useCallback(
(forward = true) => {
let index = currentTemplate;
index = forward ? index + 1 : index - 1;
if (index > templates.length - 1) {
index = 0;
} else if (index < 0) {
index = templates.length - 1;
}
setCurrentTemplate(index);
},
// Regenerate the callback whenever `currentTemplate` or `setCurrentTemplate` changes
[currentTemplate, setCurrentTemplate]
);