Czy konieczne jest użycie operatora spreadu w użyciu reduktora?

Nov 20 2020

EDYCJA: po sprawdzeniu odpowiedzi Briana dowiedziałem się, że nawet w drugim przykładzie usunięcie ...statespowodowałoby takie samo zachowanie jak w pierwszym przykładzie, więc to pytanie było błędne. Nie jestem pewien, co spowodowało niewłaściwe zachowanie i przepraszam za wszelkie niedogodności. Odpowiedź Briana jest dobrym wyjaśnieniem zastosowania.

Porównaj następujące 2 fragmenty kodu, robią to samo, aktualizując 2 stany naraz za pomocą initialState, z wyjątkiem tego, że jeden używa, useStatea drugi używa useReducer:

  1. useState

const {useState} = React;

const initialState = {
  name: 'John',
  age: 20
};

const Example = () => {
  const [state, setState] = useState(initialState);

  const handleName = () => {
    setState({...state, name: 'Tom'});
  }
  
  const handleAge = () => {
    setState({...state, age: 30});
  }

  return (
    <div>
      <p>{state.name}</p>
      <p>{state.age}</p>
      <button onClick={handleName}>
        Click to change name
      </button>
      <button onClick={handleAge}>
        Click to change age
       </button>
    </div>
  );
};

ReactDOM.render(
  <Example />,
  document.getElementById("react")
);
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.8.4/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.8.4/umd/react-dom.production.min.js"></script>
<div id="react"></div>

  1. useReducer

const {useReducer} = React;

const initialState = {
    name: 'Tom',
    age: 30
}
const reducer = (state, action) => {
    switch (action.type) {
        case 'name':
            return {
                ...state,
                name: 'John'
            }
        case 'age':
            return {
                ...state,
                age: 25
            }
        default:
            return state
    }
}

const Example = () => {
  const [state, dispatch] = useReducer(reducer, initialState);
    return (
        <div>
            <p>Name: {state.name}</p>
            <p>Age: {state.age}</p>
            <button onClick={() => dispatch({ type: 'name' })}>Click to update name</button>
            <button onClick={() => dispatch({ type: 'age' })}>Click to update age</button>
        </div>
    )
};

ReactDOM.render(
  <Example />,
  document.getElementById("react")
);
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.8.4/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.8.4/umd/react-dom.production.min.js"></script>
<div id="react"></div>

Jeśli usuniesz ...statew pierwszym przykładzie, ustawia stan na pojedynczą parę klucz / wartość, więc nie działa. Spodziewałem się tego samego w drugim przykładzie, ale tak się nie stało. drugi przykład działa dobrze po usunięciu operatora spreadu.

Mam 3 pytania do drugiego przykładu:

  1. jaki jest pożytek z operatora spreadu?
  2. dlaczego nadal działa po usunięciu operatorów rozsiewacza?
  3. Jeśli działa dobrze bez operatora spreadu, czy to oznacza, że ​​operator spreadu nie jest potrzebny w drugim przykładzie?

Odpowiedzi

1 BrianThompson Nov 24 2020 at 17:17

Rozumiem, że jest to bardzo podobne pytanie do tego , ale i tak daję odpowiedź, aby miejmy nadzieję, że przyszli czytelnicy nie otrzymają nieprawidłowych informacji.

Ani useStateteż useReducernie łączy się w ogóle poprzednich wartości. Za zachowanie poprzednich wartości stanu odpowiada użytkownik podczas aktualizacji stanu (w setStatewywołaniu lub w definicji funkcji reduktora).

Możesz to wyraźnie zobaczyć w zmodyfikowanym kodzie poniżej. Wszystko, co zmieniłem, to usunięcie ...stateobudowy z każdego przełącznika.

const {useReducer} = React;

const initialState = {
    name: 'Tom',
    age: 30
}
const reducer = (state, action) => {
    switch (action.type) {
        case 'name':
            return {
                name: 'John'
            }
        case 'age':
            return {
                age: 25
            }
        default:
            return state
    }
}

const Example = () => {
  const [state, dispatch] = useReducer(reducer, initialState);
    return (
        <div>
            <p>Name: {state.name}</p>
            <p>Age: {state.age}</p>
            <button onClick={() => dispatch({ type: 'name' })}>Click to update name</button>
            <button onClick={() => dispatch({ type: 'age' })}>Click to update age</button>
        </div>
    )
};

ReactDOM.render(
  <Example />,
  document.getElementById("react")
);
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.8.4/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.8.4/umd/react-dom.production.min.js"></script>
<div id="react"></div>

Powodem, dla którego operator spreadu jest tak często używany w tym celu, jest najmniej żmudna metoda umieszczania wszystkich niezmienionych wartości w nowym obiekcie.