如何在每次渲染时从 运行 中避免 useReducer(reducer, getInitialState()) 中使用的 getInitialState() 函数?

How to avoid getInitialState() function used in useReducer( reducer, getInitialState() ) from running on every render?

请参阅下面的代码段。

通过这样做:

React.useState(()=>getInitialState2());

您避免运行在每个渲染的useState中使用getInitialState2()

但这似乎对 useReducer 挂钩不起作用。

问题

有没有办法避免 运行 在每个渲染的 useReducer 挂钩上的 initialState 参数中使用的函数?

function App() {
  
  const [state,dispatch] = React.useReducer(reducer,getInitialState());
  const [state2,setState2] = React.useState(()=>getInitialState2());
  
  return(
    <React.Fragment>
      <div>
        State: {state}
      </div>
      <div>
        State2: {state2}
      </div>
      <div>
        <button onClick={() => dispatch({type: "INCREMENT"})}>+</button>
        <button onClick={() => dispatch({type: "DECREMENT"})}>-</button>
      </div>
    </React.Fragment>
  );
}

function getInitialState() {
  console.log("From getInitialState...");
  return 0;
}

function getInitialState2() {
  console.log("From getInitialState2...");
  return 0;
}

function reducer(state,action) {
  switch(action.type) {
    case "INCREMENT": {
      return state + 1;
    }
    case "DECREMENT": {
      return state - 1;
    }
    default: {
      return state;
    }
  }
}

ReactDOM.render(<App/>, document.getElementById("root"));
<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"/>

通过调用它,您可以在每次渲染时 运行 它。要使 initialisation lazy,传递一个函数。另外,lazy init 是 useReducer 的第 3 个参数。无论您作为 useReducer 的第二个参数提供什么,都将传递给惰性初始化函数,但您可以忽略它。

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

或者用箭头函数包裹它,如果你需要用 props 的值初始化它:

const [state,dispatch] = React.useReducer(reducer, null, () => getInitialState(props.something));

演示:

function App() {
  
  const [state,dispatch] = React.useReducer(reducer,null,getInitialState);
  const [state2,setState2] = React.useState(()=>getInitialState2());
  
  return(
    <React.Fragment>
      <div>
        State: {state}
      </div>
      <div>
        State2: {state2}
      </div>
      <div>
        <button onClick={() => dispatch({type: "INCREMENT"})}>+</button>
        <button onClick={() => dispatch({type: "DECREMENT"})}>-</button>
      </div>
    </React.Fragment>
  );
}

function getInitialState() {
  console.log("From getInitialState...");
  return 0;
}

function getInitialState2() {
  console.log("From getInitialState2...");
  return 0;
}

function reducer(state,action) {
  switch(action.type) {
    case "INCREMENT": {
      return state + 1;
    }
    case "DECREMENT": {
      return state - 1;
    }
    default: {
      return state;
    }
  }
}

ReactDOM.render(<App/>, document.getElementById("root"));
<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"/>

由于每次渲染发生时组件都会重新运行,一种选择是仅在组件外部 检索一次初始状态:

const initialState1 = getInitialState();
function App() {
  
  const [state,dispatch] = React.useReducer(reducer, initialState1);
  const [state2,setState2] = React.useState(()=>getInitialState2());
  
  return(
    <React.Fragment>
      <div>
        State: {state}
      </div>
      <div>
        State2: {state2}
      </div>
      <div>
        <button onClick={() => dispatch({type: "INCREMENT"})}>+</button>
        <button onClick={() => dispatch({type: "DECREMENT"})}>-</button>
      </div>
    </React.Fragment>
  );
}

function getInitialState() {
  console.log("From getInitialState...");
  return 0;
}

function getInitialState2() {
  console.log("From getInitialState2...");
  return 0;
}

function reducer(state,action) {
  switch(action.type) {
    case "INCREMENT": {
      return state + 1;
    }
    case "DECREMENT": {
      return state - 1;
    }
    default: {
      return state;
    }
  }
}

ReactDOM.render(<App/>, document.getElementById("root"));
<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" />