в чем разница между setCount(prev => prev 1) и setCount(count 1)?

#reactjs

#reactjs

Вопрос:

Я читаю пример реактивных перехватов. Я нахожу, что они пишут

 setCount(count   1)
  

Но я обычно писал так

 setCount(prev => prev   1)
  

Есть ли какая-либо разница? Какой из них лучше?

Комментарии:

1. @Adicia, первый поставляется с «Гарантией», а второй — с «Гарантией».

Ответ №1:

Существует разница, во-первых, счетчик будет основан на текущем значении на момент, когда этот рендеринг произошел из-за закрытия функции.

Второй всегда будет использовать последнее значение для приращения.

Поскольку замыкания — сложная тема, вот несколько примеров. Первый показывает основное различие между ними.

Во втором примере показано несколько способов, которые позволят правильно работать с замыканиями и эффектами / крючками

 const { useState, useEffect } = React;
function Example(){
const [count1, setCount1] = useState(0);
useEffect(()=>{
  setCount1(count1   1);
  setCount1(count1   1);
  setCount1(count1   1);
},[])

const [count2, setCount2] = useState(0);
useEffect(()=>{
  setCount2(prev=> prev   1);
  setCount2(prev=> prev   1);
  setCount2(prev=> prev   1);
},[])
return <div>
Both count1 and count2 have had 3 increments.
<br/>
count1 stays at 1 because the count1 variable in the useEffect isn't change due to the closure in the arrow function in the useEffect
<br/>
Current count1: {count1}
<br/>
Current count2: {count2}
</div>
}
ReactDOM.render(<Example/>,document.getElementById('root'))  
 <script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.13.1/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.13.1/umd/react-dom.production.min.js"></script>
<div id="root"/>  

 const { useState, useEffect, useRef } = React;
function Example(){
const [count1, setCount1] = useState(0);
const [count2, setCount2] = useState(0);
const [count3, setCount3] = useState(0);

const count3Ref = useRef(count3);
count3Ref.current = count3;

useEffect(()=>{
  const id = setInterval(()=>{
  setCount1(count1 1);
  setCount2(prev=>prev 1);
  setCount3(count3Ref.current 1);
  },300)
  return ()=>{clearInterval(id)}
},[])

const [count4, setCount4] = useState(0);
useEffect(()=>{
  const id = setTimeout(()=>{
  setCount4(count4 1);
  },300)
  return ()=>{clearTimeout(id)}
},[count4])
return <div>
All of the counts theoretically increment every 300ms
<br/>
<br/>
count1 stays at 1 because the count1 variable in the useEffect isn't change due to the closure in the arrow function in the useEffect
<br/>

Current count1: {count1}
<hr/>
count2 uses the functional version of setCount2 so it always uses the latest version and will update properly
<br/>
Current count2: {count2}
<hr/>
count3 increments because refs are mutable by nature and allow us to bypass the closure.
<br/>
Current count3: {count3}
<hr/>
Another possiblity: count4 increments because we properly use the dependency array and force the useEffect to re-run every time count4 changes.
<br/>
Current count4: {count4}

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

Ответ №2:

Ну, это зависит от ситуации.

Если вы хотите обновить состояние, значения которого зависят от предыдущего состояния, вам следует использовать ниже.

 setCount(prev => prev   1);
  

Это обновит состояние до нового состояния.

Если вы используете это, как показано ниже, то он обновит счетчик один, но не два раза, потому setState что является асинхронным по своей природе.

 setCount(count   1);
setCount(count   1);
  

Но если вы сделаете это

 setCount(count => count   1);
setCount(count => count   1);
  

Затем он обновит состояние два раза, поскольку мы обновляем состояние из предыдущего состояния.

 const {useState} = React;

const Example = () => {

  const [count, setCount] = useState(0);

  const handleClick = () => {
    setCount(count => count   1);
    setCount(count => count   1);
  }

  return ( 
  <div>
    <p> {count} </p> 
    <button onClick = {handleClick}> Add by 2 </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>  

Ответ №3:

Я скопировал примечание к React Hook В отличие от метода setState, найденного в компонентах класса, useState не объединяет объекты обновления автоматически. Вы можете повторить это поведение, объединив форму обновления функции с синтаксисом распространения объекта:

 setState(prevState => {
  // Object.assign would also work
  return {...prevState, ...updatedValues};
});
  

Другим вариантом является useReducer, который больше подходит для управления объектами состояния, которые содержат несколько вложенных значений.

Ответ №4:

В большинстве случаев они выполняют одну и ту же работу.

Но второй вариант лучше, когда ваш компонент react становится сверхсложным. Потому count что в первом случае ненадежно, если у вас много setState() очередей. Это может быть какое-то значение, которого вы не ожидаете. setCount(prev => prev 1) всегда добавляйте 1 к его предыдущему значению, что является более надежным.

На самом деле вы можете передать (previousValue, props) в качестве параметров setState() .

Комментарии:

1. setState((previousValue, props)=>{}) Вещь, хотя и очень аккуратная (и я об этом не знал), является функцией компонента класса, а не useState функцией перехвата.

Ответ №5:

setState() это асинхронная операция. Реагирующие пакеты изменяют состояние, чтобы уменьшить количество повторных рендерингов. (особенно в React 18 с автоматической пакетной обработкой)

Состояние может не измениться сразу после setState() вызова.

Поэтому предпочтительным методом является обновление состояния с prevState помощью вместо того, чтобы полагаться на текущее состояние.