[reactjs] 반응 후크에서`setState` 콜백을 사용하는 방법

React hooks는 useState구성 요소 상태를 설정하기 위해 도입 되었습니다. 그러나 후크를 사용하여 아래 코드와 같이 콜백을 대체하려면 어떻게해야합니까?

setState(
  { name: "Michael" },
  () => console.log(this.state)
);

상태가 업데이트 된 후에 뭔가를하고 싶습니다.

useEffect추가 작업을 수행하는 데 사용할 수 있지만 비트 코드가 필요한 이전 상태 값을 확인해야합니다. useState후크 와 함께 사용할 수있는 간단한 솔루션을 찾고 있습니다 .



답변

이를 위해서는 useEffect후크를 사용해야합니다.

const [counter, setCounter] = useState(0);

const doSomething = () => {
  setCounter(123);
}

useEffect(() => {
   console.log('Do something after counter has changed', counter);
}, [counter]);


답변

이전 상태를 업데이트하려면 후크에서 다음과 같이 할 수 있습니다.

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


setCount(previousCount => previousCount + 1);


답변

useEffect, 상태 업데이트 에서만 발생 합니다 .

const [state, setState] = useState({ name: "Michael" })
const isFirstRender = useRef(true)

useEffect(() => {
  if (!isFirstRender.current) {
    console.log(state) // do something after state has updated
  }
}, [state])

useEffect(() => {
  isFirstRender.current = false // toggle flag after first render/mounting
}, [])

위는 setState콜백을 가장 잘 모방하고 초기 상태 에서는 실행되지 않습니다 . 여기에서 사용자 지정 후크를 추출 할 수 있습니다.

function useEffectUpdate(effect, deps) {
  const isFirstRender = useRef(true)

  useEffect(() => {
    if (!isFirstRender.current) {
      effect()
    }
  }, deps) // eslint-disable-line react-hooks/exhaustive-deps

  useEffect(() => {
    isFirstRender.current = false;
  }, []);
}

// ... Usage inside component
useEffectUpdate(() => { console.log(state) }, [state])


답변

사용 useEffect은 직관적 인 방법이 아니라고 생각 합니다.

나는 이것을 위해 래퍼를 만들었습니다. 이 커스텀 후크에서는 setState매개 변수 대신 매개 변수로 콜백을 전송할 수 있습니다 useState.

방금 Typescript 버전을 만들었습니다. 따라서 Javascript에서 이것을 사용해야하는 경우 코드에서 유형 표기법을 제거하십시오.

용법

const [state, setState] = useStateCallback(1);
setState(2, (n) => {
  console.log(n) // 2
});

선언

import { SetStateAction, useCallback, useEffect, useRef, useState } from 'react';

type Callback<T> = (value?: T) => void;
type DispatchWithCallback<T> = (value: T, callback?: Callback<T>) => void;

function useStateCallback<T>(initialState: T | (() => T)): [T, DispatchWithCallback<SetStateAction<T>>] {
  const [state, _setState] = useState(initialState);

  const callbackRef = useRef<Callback<T>>();
  const isFirstCallbackCall = useRef<boolean>(true);

  const setState = useCallback((setStateAction: SetStateAction<T>, callback?: Callback<T>): void => {
    callbackRef.current = callback;
    _setState(setStateAction);
  }, []);

  useEffect(() => {
    if (isFirstCallbackCall.current) {
      isFirstCallbackCall.current = false;
      return;
    }
    callbackRef.current?.(state);
  }, [state]);

  return [state, setState];
}

export default useStateCallback;

약점

전달 된 화살표 함수가 변수 외부 함수를 참조하는 경우 상태가 업데이트 된 후 값이 아닌 현재 값을 캡처합니다. 위의 사용 예에서 console.log (state) 는 2가 아닌 1을 인쇄합니다.


답변

setState() 컴포넌트 상태의 변경 사항을 대기열에 추가하고이 컴포넌트와 그 자식을 업데이트 된 상태로 다시 렌더링해야한다고 React에 알립니다.

setState 메서드는 비동기 적이며 사실 약속을 반환하지 않습니다. 따라서 함수를 업데이트하거나 호출하려는 경우 setState 함수에서 두 번째 인수로 해당 함수를 콜백이라고 부를 수 있습니다. 예를 들어, 위의 경우 함수를 setState 콜백으로 호출했습니다.

setState(
  { name: "Michael" },
  () => console.log(this.state)
);

위의 코드는 클래스 컴포넌트에 대해 잘 작동하지만 함수 컴포넌트의 경우 setState 메소드를 사용할 수 없으며 동일한 결과를 얻기 위해 use effect hook을 사용할 수 있습니다.

분명한 방법은 ypu가 useEffect와 함께 사용할 수 있다는 것입니다.

const [state, setState] = useState({ name: "Michael" })

useEffect(() => {
  console.log(state) // do something after state has updated
}, [state])

그러나 이것은 첫 번째 렌더링에서도 발생하므로 첫 번째 렌더링 이벤트를 확인하고 상태 렌더링을 피할 수있는 코드를 다음과 같이 변경할 수 있습니다. 따라서 구현은 다음과 같은 방법으로 수행 할 수 있습니다.

여기에서 사용자 후크를 사용하여 첫 번째 렌더링을 식별 할 수 있습니다.

useRef 후크를 사용하면 기능적 구성 요소에서 가변 변수를 만들 수 있습니다. DOM 노드 / React 요소에 액세스하고 다시 렌더링을 트리거하지 않고 변경 가능한 변수를 저장하는 데 유용합니다.

const [state, setState] = useState({ name: "Michael" });
const firstTimeRender = useRef(true);

useEffect(() => {
 if (!firstTimeRender.current) {
    console.log(state);
  }
}, [state])

useEffect(() => {
  firstTimeRender.current = false
}, [])


답변

내 설정에서 useEffect를 사용하여 동일한 문제가 발생했습니다 (배열 여러 하위 구성 요소에서 부모의 상태를 업데이트하고 어떤 구성 요소가 데이터를 업데이트했는지 알아야 함).

promise에 setState를 래핑하면 완료 후 임의의 작업을 트리거 할 수 있습니다.

import React, {useState} from 'react'

function App() {
  const [count, setCount] = useState(0)

  function handleClick(){
    Promise.resolve()
      .then(() => { setCount(count => count+1)})
      .then(() => console.log(count))
  }


  return (
    <button onClick= {handleClick}> Increase counter </button>
  )
}

export default App;

다음 질문은 저를 올바른 방향으로 인도합니다.
후크를 사용할 때 React 배치 상태가 업데이트됩니까?


답변

귀하의 질문은 매우 유효합니다. useEffect가 기본적으로 한 번 실행되고 종속성 배열이 변경 될 때마다 실행된다는 점을 말씀 드리겠습니다.

아래 예를 확인하십시오. :

import React,{ useEffect, useState } from "react";

const App = () => {
  const [age, setAge] = useState(0);
  const [ageFlag, setAgeFlag] = useState(false);

  const updateAge = ()=>{
    setAgeFlag(false);
    setAge(age+1);
    setAgeFlag(true);
  };

  useEffect(() => {
    if(!ageFlag){
      console.log('effect called without change - by default');
    }
    else{
      console.log('effect called with change ');
    }
  }, [ageFlag,age]);

  return (
    <form>
      <h2>hooks demo effect.....</h2>
      {age}
      <button onClick={updateAge}>Text</button>
    </form>
  );
}

export default App;

후크와 함께 setState 콜백을 실행하려면 플래그 변수를 사용하고 useEffect 내부에 IF ELSE OR IF 블록을 제공하여 해당 조건이 충족되면 해당 코드 블록 만 실행되도록합니다. 그러나 시간 효과는 종속성 배열 변경으로 실행되지만 효과 내부의 IF 코드는 해당 특정 조건에서만 실행됩니다.