혼자 적어보는 노트

[React] 비동기 setState 연속 처리 시 오류 본문

React

[React] 비동기 setState 연속 처리 시 오류

jinist 2021. 12. 15. 00:49

react에서 state를 변경하기위해

this.setState나 useState에서 setState를 사용하게 되는데

비동기적으로 업데이트되는 특성 때문에 종종 원하지 않는 결과를 보여줄 때가 있다.

 

setState를 동기적으로 호출하기 위해

this.setState의 두번 째 인자에 콜백 함수를 넣어 동기적으로 실행하게 하거나

useState와 useEffect를 사용하여 setState가 실행된 후를 감지할 수는 있으나

 

개별적으로 나누어진 함수 안에서 state를 함께 변경 할때 

state변경하기가 어려웠다.

 

아래의 코드는 직접 문제를 겪게된 코드이다.

 

  const addHistory = (keyword) => {
    if (!keyword) {
      return;
    }
    const hasHistory = historyList.some((item) => item.keyword === keyword);
    
    if (hasHistory) removeHistory(keyword);
    // history에 중복된 값이 있다면 history를 지우는 함수 실행
    
    const history = { id: createNextId(historyList), keyword };
    setHistoryList([history, ...historyList]); //* 문제발생 부분 1
  };

  const removeHistory = (keyword) => {
    const history = historyList.filter((item) => item.keyword !== keyword);
    // keyword로 받은 값이 중복된다면 제거후 새로운 object로 반환
    setHistoryList(history); //* 문제발생 부분 2
  };

addHistory라는 함수 안에서 hasHistory로 중복된 keyword확인 후 값이 있다면

removeHistory를 실행해서 값을 지우고 새롭게 history를 상단에 추가하는 기능을 구현하고자 했다.

 

하지만 removeHistory에서 실행된 setHistoryList()의 실행이 되지 않고

addHistory에서 실행한 SetHistoryList()만 실행이 되는 것이다.

 

즉 값이 지워지지 않은 상태에서 추가만 되는 상황이다.

 

[해결방법]

setState사용시 이전 state를 인자로 받고 새로운 state반환

 

const addHistory = (keyword) => {
    // .. 생략
    if (hasHistory) {
      removeHistory(keyword);
    }
    const newHistory = { id: createNextId(historyList), keyword };
    setHistoryList((historyList) => [newHistory, ...historyList]);
    //prevState를 인자로 받고 새로운 state를 반환해준다
  };

  const removeHistory = (keyword) => {
    const history = historyList.filter((item) => item.keyword !== keyword);
    setHistoryList(history); 
    // 해당 Setstate먼저 실행
  };

위와 같이 setState에 함수를 인자를 넣어서 실행 시

원하는대로 removeHistory()로 값이 지워진 후 addHistory()의 setHistoryList가 실행되었다.

 

이것으로 알게된 사실은

함수형 setState()에는 이전의 state를 인자로 받고 새로운 state 객체를 반환할 수 있다는 것이다.

 

  const [test, setTest] = useState(0);
  
  const testClick = () => {
    setTest(test + 1);
    setTest(test + 2);
    setTest(test + 3);
  };
// 연속된 setState로 merge되어
// test에는 맨 하단의 변경 값인 3이 저장된다

  const testClick = () => {
    setTest((prevState) => prevState + 1);
    setTest((prevState) => prevState + 2);
    setTest((prevState) => prevState + 3);
  };
// 이전의 state를 참조하게 되어 test에는 6의 값이 저장된다

 

Comments