useReducer - keydown 处理程序重新评估每个渲染的初始状态
useReducer - keydown handler re-evaluates initial state on each render
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>;
}
当一个组件发现它需要 re-render 时,它的功能将再次 运行。你这样做相当于问为什么
export default function App() {
// ...
const [state, dispatch] = useReducer(reducer, getInitialState());
运行s getInitialState
每次App 运行s,相当于
export default function App() {
// ...
const parameterToPass = getInitialState();
const [state, dispatch] = useReducer(reducer, parameterToPass);
这应该让事情变得清楚。
并不是说 useReducer
需要 再次计算初始值 - 它不需要,它只在第一次使用第二个参数 运行s,确定初始状态。就是你在每次 App re-renders.
时调用计算初始状态的函数
只要你确定计算初始状态的函数没有side-effects,就可以忽略它——你现在做的就好了。
另一种选择是将第三个参数传递给 useReducer
作为调用以计算初始状态的函数。
const [state, dispatch] = useReducer(reducer, null, getInitialState);
您在每次渲染时都调用了 getInitialState,这并不意味着它正在获取 re-initialized。
您可以通过 运行 为函数使用而设计的第 3 个参数位置的初始化器来避免这种情况。
您还应该在渲染函数之外为 reducer 创建函数,否则它 re-runs 比它应该的更频繁。
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>
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>;
}
当一个组件发现它需要 re-render 时,它的功能将再次 运行。你这样做相当于问为什么
export default function App() {
// ...
const [state, dispatch] = useReducer(reducer, getInitialState());
运行s getInitialState
每次App 运行s,相当于
export default function App() {
// ...
const parameterToPass = getInitialState();
const [state, dispatch] = useReducer(reducer, parameterToPass);
这应该让事情变得清楚。
并不是说 useReducer
需要 再次计算初始值 - 它不需要,它只在第一次使用第二个参数 运行s,确定初始状态。就是你在每次 App re-renders.
只要你确定计算初始状态的函数没有side-effects,就可以忽略它——你现在做的就好了。
另一种选择是将第三个参数传递给 useReducer
作为调用以计算初始状态的函数。
const [state, dispatch] = useReducer(reducer, null, getInitialState);
您在每次渲染时都调用了 getInitialState,这并不意味着它正在获取 re-initialized。
您可以通过 运行 为函数使用而设计的第 3 个参数位置的初始化器来避免这种情况。
您还应该在渲染函数之外为 reducer 创建函数,否则它 re-runs 比它应该的更频繁。
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>