【问题标题】:How to use useReducer instead of useState?如何使用 useReducer 而不是 useState?
【发布时间】:2020-05-16 11:39:38
【问题描述】:

您好,我的初始状态为空对象,

let [value,setValue] = useState({});

function onSubmit(){
  setValue({"new Object"})
}

并在单击按钮时更新它。有没有办法在 useReducer 中实现这个场景,而且在这里我想创建一个新状态而不是改变它。非常感谢您的帮助或建议。

【问题讨论】:

  • 您问题中的代码不保证使用 useReducer,useState 可以与给定的代码一起正常工作。

标签: reactjs react-hooks use-reducer


【解决方案1】:

两个钩子都定义了一个初始状态,并返回一个 setter 和一个 getter 来处理状态:

  [value,fn] = useReducer(...); // or useState(...);

但是,在上面的代码 sn-p 中,fn 的行为不同于 useReduceruseState。另外,useReducer 需要 2 个参数,而 useState 只需要 1 个。基本上:

  • useReducer:需要 2 个参数,(a) 一个函数(称为 reducer 函数),以及 (b) 状态的初始值。每当您使用某个值调用fn 时,都会调用reducer 函数来修改状态。而传递给fn的值会被传递给reducer函数(作为第二个参数)。

  • useState:只需要 1 个参数,即状态的初始值。它只会将状态替换为作为参数传递给fn 的值(您也可以将一个函数传递给fn,它接收当前状态作为参数并返回新状态)。

useState 示例:

const { useState } = React;

const App = () => {
  const initialState = { called: false, name: 'Jane' };
  const [value, setValue] = useState(initialState);
  const replaceState = () => {
    setValue({ 
      called: true, 
      fullName: `${value.name ? value.name : 'Just'} Fonda` 
    });
  }
  return (
    <div className="App">
      <button onClick={replaceState}>Replace Name</button>
      <button onClick={(e) => setValue(initialState)}>Reset</button>
      <pre>{JSON.stringify(value)}</pre>
      {value.called 
       ? <pre>Notice that there's no 'name' attribute anymore</pre> 
       : null}
    </div>
  );
}

ReactDOM.render(<App />,root);
<script crossorigin src="https://unpkg.com/react@16/umd/react.development.js"></script>
<script crossorigin src="https://unpkg.com/react-dom@16/umd/react-dom.development.js"></script>

<div id="root"></div>

useReducer 示例:

您必须构建一个将修改状态的函数(reducer)。该函数必须接收 2 个参数:实际状态和修改状态所需的任何值。

此外,此函数必须返回一个值,React 将设置为新状态。

如果您曾经使用过 redux 模式,那么您一定对这种行为很熟悉。所以,如果你和大家一样,想坚持 redux 模式,使用 action 作为 reducer 函数的第二个参数的名称(将用于修改状态)。它将是一个包含 2 个属性的对象:typepayload

reducer 内部的逻辑处理动作,通过使用type(字符串)来决定如何使用payload(任意)修改状态。在减速器内有一个switch-case 并不少见。所以,让我们按照传统,使用这种类似 redux 的模式构建一个 reducer。

const { useReducer } = React;

// Notice the two parameters of the reducer:
//   state: current state
//   payload: object with the shape {type: string, payload: any}
// Also, notice that the reducer MUST return a value to be used
//   as the new state. Remember that if you return nothing
//   javascript will return undefined
const reducer = (state, action) => {
  switch(action.type) {
    case 'inc': return state + action.payload;
    case 'dec': return state - action.payload;

    // for any other type, return the current state
    default: return state;
  }
}

const App = () => {
  // specify the reducer and an initial value for the state
  const [value, setValue] = useReducer(reducer, 0);

  const inc = (e) => setValue({type: 'inc', payload: 1});
  const dec = (e) => setValue({type: 'dec', payload: 1});

  return (
    <div className="App">
      <button onClick={inc}>Increment</button>
      <button onClick={dec}>Decrement</button>
      Counter:{value}
    </div>
  );
}

ReactDOM.render(<App />,root);
<script crossorigin src="https://unpkg.com/react@16/umd/react.development.js"></script>
<script crossorigin src="https://unpkg.com/react-dom@16/umd/react-dom.development.js"></script>

<div id="root"></div>

作为最后的附注,我在上面的 sn-p 中使用的 setValue 名称甚至与 useReducer 返回的函数所使用的最常用名称都不接近。更常见的是称它为dispatch

【讨论】:

    【解决方案2】:

    useReducer 使用的reducer 是一个以当前状态调用的函数,以及一个改变状态的动作。动作可以是任何你想要的。

    您可以通过从 reducer 返回新状态(动作)轻松地使用 useReducer 创建 useState

    const { useReducer } = React;
    
    function reducer(_, newState) {
      return newState;
    }
    
    function App() {
      const [value, setValue] = useReducer(reducer, { a: "2" });
    
    
      function onSubmit() {
        setValue({ b: 2 });
      }
      return (
        <div className="App">
          <button onClick={onSubmit}>onSubmit</button>
          {JSON.stringify(value)}
        </div>
      );
    }
    
    ReactDOM.render(
      <App />,
      root
    );
    <script crossorigin src="https://unpkg.com/react@16/umd/react.development.js"></script>
    <script crossorigin src="https://unpkg.com/react-dom@16/umd/react-dom.development.js"></script>
    
    <Div id="root"></div>

    不过,如果只需要替换当前状态,useState 就足够了。但是,如果您想通过替换它来添加到当前状态,useReducer 是一个不错的选择:

    const { useReducer } = React;
    
    function reducer(state, newState) {
      return {
        ...state,
        ...newState
      };
    }
    
    function App() {
      const [value, setValue] = useReducer(reducer, { a: "1" });
    
    
      function onSubmit() {
        setValue({ b: 2 });
      }
      return (
        <div className="App">
          <button onClick={onSubmit}>onSubmit</button>
          {JSON.stringify(value)}
        </div>
      );
    }
    
    ReactDOM.render(
      <App />,
      root
    );
    <script crossorigin src="https://unpkg.com/react@16/umd/react.development.js"></script>
    <script crossorigin src="https://unpkg.com/react-dom@16/umd/react-dom.development.js"></script>
    
    <Div id="root"></div>

    通过reducer后的状态是{ a: 1, b: 2 }

    经典的方法是传递带有类型和有效负载的动作。根据类型,reducer 可以“决定”如何处理有效载荷,以及当前状态来生成新状态。见useReducer api documentation

    【讨论】:

    • 我在 codeSandbox 中添加了您的代码,我无法获得初始状态 onload 。你能看看那个codesandbox.io/s/patient-cherry-mvie8?file=/src/App.js
    • @DhanushKumarS - 在答案中添加了 sn-p。在您的代码中将 useState 替换为 useReducer。我的回答有误。
    猜你喜欢
    • 2019-07-05
    • 1970-01-01
    • 2020-06-27
    • 1970-01-01
    • 1970-01-01
    • 2020-09-16
    • 1970-01-01
    • 1970-01-01
    • 2020-03-10
    相关资源
    最近更新 更多