useReducer-keydown处理程序在每次渲染时重新评估初始状态



https://codesandbox.io/s/suspicious-chebyshev-vy4mf2

我有一个useReducer钩子和一个带有keydown事件处理程序的useEffect。问题是,在处理键盘按钮按下后的每次重新渲染中,组件函数都会重新运行获得初始状态的函数。

可见的错误结果是每次渲染时在控制台中记录的随机数(我希望没有记录(。换句话说,这里的目标是停止在每个渲染上运行getInitialState函数。

import { useEffect, useReducer } from "react";
import "./styles.css";
interface IAction {
type: string;
payload: string;
}
export default function App() {
const reducer = (state: string, action: IAction) => {
switch (action.type) {
case "KEYDOWN":
return action.payload;
default:
return state;
}
};
const getInitialState = () => {
const rand = Math.random().toString();
console.log(rand);
return rand;
};
const [state, dispatch] = useReducer(reducer, getInitialState());
const keyDownHandler = (e: KeyboardEvent): void => {
dispatch({
type: "KEYDOWN",
payload: e.key
});
};
useEffect(() => {
document.addEventListener("keydown", keyDownHandler);
return () => document.removeEventListener("keydown", keyDownHandler);
}, [state, dispatch]);
return <div className="App">{state}</div>;
}

当组件发现需要重新渲染时,其函数将再次运行。你所做的相当于问为什么

export default function App() {
// ...
const [state, dispatch] = useReducer(reducer, getInitialState());

每次运行应用程序时运行getInitialState,相当于

export default function App() {
// ...
const parameterToPass = getInitialState();
const [state, dispatch] = useReducer(reducer, parameterToPass);

这应该会让事情变得清楚。

这并不是说useReducer需要来再次计算初始值——它没有,它只是在第一次运行时使用第二个参数来确定初始状态。这是因为你调用的函数会在每次应用程序重新渲染时计算初始状态。

只要你确保计算初始状态的函数没有副作用,就可以忽略它——你现在所做的一切都很好。

另一种选择是将第三个参数传递给useReducer,作为要调用以计算初始状态的函数。

const [state, dispatch] = useReducer(reducer, null, getInitialState);

您在每次渲染时都调用getInitialState,这并不意味着它被重新初始化。

您可以通过在第三个参数位置运行初始值设定项来避免这种情况,该位置是为函数使用而设计的。

您还应该在渲染函数之外为reducer创建函数,否则它会比应该的频率更频繁地重新运行。

const { useEffect, useReducer } = React;
interface IAction {
type: string;
payload: string;
}
const reducer = (state: string, action: IAction) => {
switch (action.type) {
case "KEYDOWN":
return action.payload;
default:
return state;
}
};
function App() {
const getInitialState = () => {
const rand = Math.random().toString();
console.log(rand);
return rand;
};
const [state, dispatch] = useReducer(reducer, null, getInitialState);
const keyDownHandler = (e: KeyboardEvent): void => {
dispatch({
type: "KEYDOWN",
payload: e.key
});
};
useEffect(() => {
document.addEventListener("keydown", keyDownHandler);
return () => document.removeEventListener("keydown", keyDownHandler);
}, [state, dispatch]);
return <div className="App">{state}</div>;
}
const rootElement = document.getElementById("root");
ReactDOM.render(<App />, rootElement);
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.8.0/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.8.0/umd/react-dom.production.min.js"></script>
<div id="root"></div>

最新更新