如何使用useReducer代替useState?

时间:2020-05-16 11:39:38

标签: reactjs react-hooks

嗨,我的初始状态是空对象,

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

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

并在单击按钮的“提交”上对其进行更新。有什么方法可以在useReducer中实现此方案,并且在这里我想创建一个新状态,而不是对其进行更改。非常感谢您的帮助或建议。

2 个答案:

答案 0 :(得分:2)

reducer使用的useReducer是用当前状态调用的函数,也是用来更改状态的动作。该动作可以是您想要的任何东西。

通过从化简器返回新状态(动作),您可以使用useState轻松创建useReducer

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>

通过减速器后的状态为{ a: 1, b: 2 }

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

答案 1 :(得分:2)

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

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

但是,在以上代码段中,setValue的行为从useReduceruseState有所不同。另外,useReducer获得2个参数,而useState仅获得1个参数。

  • useReducer:需要2个参数,(a)一个函数,(b)状态的初始值。每当您以某个值调用setValue时,都会调用该函数来修改状态。

  • useState:仅需要1个参数,即状态的初始值。它将只是用作为参数传递给setValue的值替换状态。

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个属性的对象: type payload

化简器内部的逻辑通过使用type(字符串)来决定如何使用payload(任意)来修改状态来处理该动作。在减速器中包含switch-case并不少见。因此,让我们遵循传统,使用这种类似redux的模式构建一个减速器。

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>