programing

리액트 훅에서 'setState' 콜백을 사용하는 방법

cafebook 2023. 2. 27. 22:21
반응형

리액트 훅에서 'setState' 콜백을 사용하는 방법

에 의해 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]);

「 」를 useEffect번째 렌더링 무시되는 콜백에 따라 코드를 수정합니다.

import React, { useEffect, useRef } from 'react';

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

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

useEffect(() => {
  // Return early, if this is the first render:
  if ( !didMount.current ) {
    return didMount.current = true;
  }
  // Paste code to be executed on subsequent renders:
  console.log('Do something after counter has changed', counter);
}, [counter]);

이전 상태를 갱신하는 경우는, 훅으로 다음과 같이 할 수 있습니다.

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


setCount(previousCount => previousCount + 1);

setState에 「」을 부가합니다.useEffect, 상태 갱신시에만 기동합니다(초기 상태는 기동하지 않습니다.

const [state, setState] = useState({ name: "Michael" })
const isFirstRender = useRef(true)
useEffect(() => {
  if (isFirstRender.current) {
    isFirstRender.current = false // toggle flag after first render/mounting
    return;
  }
  console.log(state) // do something after state has updated
}, [state])

useEffectUpdate

function useEffectUpdate(callback) {
  const isFirstRender = useRef(true);
  useEffect(() => {
    if (isFirstRender.current) {
      isFirstRender.current = false; // toggle flag after first render/mounting
      return;
    }
    callback(); // performing action after state has updated
  }, [callback]);
}

// client usage, given some state dep
const cb = useCallback(() => { console.log(state) }, [state]); // memoize callback
useEffectUpdate(cb);

생각에는, 내에, 용 i를 useEffect직관적인 방법이 아닙니다.

이거 포장지를 만들었어요.훅에서는, 을 「 」, 「 」에 할 수 .setState parameter를 합니다.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을 인쇄합니다.

셋업에서 useEffect를 사용해도 문제가 해결되지 않았습니다(어레이의 여러 하위 컴포넌트에서 부모 상태를 업데이트하고 있는데 어떤 컴포넌트가 데이터를 업데이트했는지 알아야 합니다).

약속으로 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 batch state update가 기능합니까?

아직 필요한 사람이 있으면 타이프로 커스텀 훅을 작성했습니다.

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

export const useStateWithCallback = <T>(initialState: T): [state: T, setState: (updatedState: React.SetStateAction<T>, callback?: (updatedState: T) => void) => void] => {
    const [state, setState] = useState<T>(initialState);
    const callbackRef = useRef<(updated: T) => void>();

    const handleSetState = (updatedState: React.SetStateAction<T>, callback?: (updatedState: T) => void) => {
        callbackRef.current = callback;
        setState(updatedState);
    };

    useEffect(() => {
        if (typeof callbackRef.current === "function") {
            callbackRef.current(state);
            callbackRef.current = undefined;
        }
    }, [state]);

    return [state, handleSetState];
}

setState()는 컴포넌트 상태에 대한 변경을 큐잉하고 React에게 이 컴포넌트와 그 하위 컴포넌트를 업데이트된 상태로 재렌더링해야 함을 알립니다.

setState 메서드는 비동기식으로 실제로는 약속을 반환하지 않습니다.따라서 함수를 업데이트 또는 호출하는 경우 setState 함수의 함수를 두 번째 인수로 콜백할 수 있습니다.예를 들어 위의 경우 함수를 setState 콜백으로 호출했습니다.

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

위의 코드는 클래스 컴포넌트에서는 정상적으로 동작하지만 기능 컴포넌트의 경우 setState 메서드를 사용할 수 없습니다.이것에 의해, use effect use effect 훅을 사용할 수 있습니다.

ypu를 useEffect와 함께 사용할 수 있는 명백한 방법은 다음과 같습니다.

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

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

그러나 첫 번째 렌더링에서도 실행되므로 첫 번째 렌더링 이벤트를 확인하고 상태 렌더링을 피할 수 있는 코드를 다음과 같이 변경할 수 있습니다.그 때문에, 실장은 다음의 방법으로 실시할 수 있습니다.

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

useRef Hook을 사용하면 기능 컴포넌트에 가변 변수를 만들 수 있습니다.이 기능은 DOM 노드/React 요소에 액세스하여 재렌더를 트리거하지 않고 가변 변수를 저장하는 데 유용합니다.

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

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

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

업데이트 후 최신 상태를 얻으려면 다음과 같은 방법을 사용할 수 있습니다.

  1. (유효효과)
    https://reactjs.org/docs/hooks-reference.html#useeffecthttpsreactjs.org/docs/.html#useeffect
    const [state, setState] = useState({name: "Michael"});
    
    const handleChangeName = () => {
      setState({name: "Jack"});
    }
    
    useEffect(() => {
      console.log(state.name); //"Jack"

      //do something here
    }, [state]);
  1. (기능 업데이트)
    https://reactjs.org/docs/hooks-reference.html#functional-updateshttpsreactjs.org/docs/hooks-reference.html#
    상태를 하여 새 할 수 ."이전 상태를 사용하여 새로운 상태를 계산하면 함수를 setState에 전달할 수 있습니다.
    const [state, setState] = useState({name: "Michael"});

    const handleChangeName = () => {
      setState({name: "Jack"})
      setState(prevState => {
        console.log(prevState.name);//"Jack"

        //do something here

        // return updated state
        return prevState;
      });
    }
  1. useRef
    https://reactjs.org/docs/hooks-reference.html#userefhttpsreactjs.org/docs/.html#useref
    참조하다'
    const [state, setState] = useState({name: "Michael"});

    const stateRef = useRef(state);
    stateRef.current  = state;
    const handleClick = () => {
      setState({name: "Jack"});

      setTimeout(() => {
        //it refers to old state object
        console.log(state.name);// "Michael";

        //out of syntheticEvent and after batch update
        console.log(stateRef.current.name);//"Jack"

        //do something here
      }, 0);
    }

에 모든 상태 됩니다.react syntheticEvent setState는 setState를 반환합니다.
는 항상컴포넌트를 하지 않습니다."setState()는 컴포넌트를 즉시 업데이트하지 않습니다. ",
https://reactjs.org/docs/react-component.html#setstatehttpsreactjs.org/docs/.html#setstate

.
리액트? 리액트?

상태 설정 후 몇 가지 파라미터API 호출을 하고 싶은 유스케이스가 있었습니다.이러한 파라미터를 자신의 상태로 설정하고 싶지 않기 때문에 커스텀 훅을 만들었습니다.이것이 해결책입니다.

import { useState, useCallback, useRef, useEffect } from 'react';
import _isFunction from 'lodash/isFunction';
import _noop from 'lodash/noop';

export const useStateWithCallback = initialState => {
  const [state, setState] = useState(initialState);
  const callbackRef = useRef(_noop);

  const handleStateChange = useCallback((updatedState, callback) => {
    setState(updatedState);
    if (_isFunction(callback)) callbackRef.current = callback;
  }, []);

  useEffect(() => {
    callbackRef.current();
    callbackRef.current = _noop; // to clear the callback after it is executed
  }, [state]);

  return [state, handleStateChange];
};

당신의 질문은 매우 타당합니다.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 콜백을 훅과 함께 실행하는 경우 flag 변수를 사용하여 useEffect 내의 IF ELSE 또는 IF 블록을 지정합니다.이 조건이 충족되면 해당 코드 블록만 실행됩니다.의존관계 배열의 변경에 따라 시간 효과가 실행되지만 IF 코드 내부 효과는 해당 특정 조건에 대해서만 실행됩니다.

훅을 쓸 수 있어요useScheduleNextRenderCallback이 명령어는 "syslog" 함수를 반환합니다.전화하고 나서setState다음 렌더링에서 실행할 콜백을 전달하여 "callback" 함수를 호출할 수 있습니다.

import { useCallback, useEffect, useRef } from "react";

type ScheduledCallback = () => void;
export const useScheduleNextRenderCallback = () => {
  const ref = useRef<ScheduledCallback>();

  useEffect(() => {
    if (ref.current !== undefined) {
      ref.current();
      ref.current = undefined;
    }
  });

  const schedule = useCallback((fn: ScheduledCallback) => {
    ref.current = fn;
  }, []);

  return schedule;
};

사용 예:

const App = () => {
  const scheduleNextRenderCallback = useScheduleNextRenderCallback();

  const [state, setState] = useState(0);

  const onClick = useCallback(() => {
    setState(state => state + 1);
    scheduleNextRenderCallback(() => {
      console.log("next render");
    });
  }, []);

  return <button onClick={onClick}>click me to update state</button>;
};

테스트 케이스 축소: https://stackblitz.com/edit/react-ts-rjd9jk

심플한 솔루션, 설치만 하면 됩니다.

npm i use-state-with-callback

import React from 'react';
import { useStateWithCallbackLazy } from "use-state-with-callback";

const initialFilters = {
  smart_filter: "",
};

const MyCallBackComp = () => {
  const [filters, setFilters] = useStateWithCallbackLazy(initialFilters);

  const filterSearchHandle = (e) => {
    setFilters(
      {
        ...filters,
        smart_filter: e,
      },
      (value) => console.log("smartFilters:>", value)
    );
  };

  return (
    <Input
      type="text"
      onChange={(e) => filterSearchHandle(e.target.value)}
      name="filter"
      placeholder="Search any thing..."
    />
  );
};

담당: USESTATE 콜백 대응

편집필

여기서 약속을 사용하면 재렌더 후 실행이 지연되고 트리거가 발생합니다.setState최신 상태를 얻으려면 두 번이 가장 좋은 해결책일 수 있습니다.왜냐하면 setState가 리스트 되기 때문에prevState사용할 수 있습니다.

오리지널 투고

Promise「 setState 」 、「 setState 。

여기 내 실험 결과야, 콜백을 사용하는 것보다 기분이 좋아

해결 하여 에서 .useEffect

function useAsyncState(initialState) {
  const [state, setState] = useState(initialState)
  const resolveCb = useRef()

  const handleSetState = (updatedState) => new Promise((resolve, reject) => {
    // force previous promise resolved
    if (typeof resolveCb.current === 'function') {
      resolveCb.current(updatedState)
    }
    resolveCb.current = resolve
    try {
      setState(updatedState)
    } catch(err) {
      resolveCb.current = undefined
      reject(err)
    }
  })

  useEffect(() => {
    if (typeof resolveCb.current === 'function') {
      resolveCb.current(state)
      resolveCb.current = undefined
    }
  }, [state])

  return [state, handleSetState]
}

컴포넌트에서 사용

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

  const increment = useMemoizedFn(async () => {
    const newCount = await setCount(count + 1)
    console.log(newCount)
  })

  console.log('rerender')

  return (
    <div>
      <h3 onClick={increment}>Hi, {count}</h3>
    </div>
  )
}

useRef를 사용하여 마운트되었는지 아닌지를 구별하는 것은 좋은 방법이 아니라고 생각합니다만, useEffect()의 genated useState() 값을 초기값인지 아닌지를 결정하는 것이 더 좋은 방법이 아닐까요?

const [val, setVal] = useState(null)

useEffect(() => {
  if (val === null) return
  console.log('not mounted, val updated', val)
}, [val])

setState 콜백을 네이티브로 지원할 때까지 플레인 javascript 방식으로 함수를 호출하여 새로운 변수를 직접 전달합니다.

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

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

  function doSomethingWCounter(newCounter) {
    console.log(newCounter); // 123
  }

없는 는, 「Ref」 에 「를할 수 .useState.

const name = useRef("John");
name.current = "Michael";
console.log(name.current); // will print "Michael" since updating the ref is not async

use-state-with-callback npm 라이브러리 및 기타 유사한 커스텀훅에 대해 알아보았지만, 결국 다음과 같은 작업을 수행할 수 있다는 것을 깨달았습니다.

const [user, setUser] = React.useState(
  {firstName: 'joe', lastName: 'schmo'}
)

const handleFirstNameChange=(val)=> {
  const updatedUser = {
     ...user,
     firstName: val
  }
  setUser(updatedUser)
  updateDatabase(updatedUser)
}

돔에서 클래스를 렌더링하고 다른 클래스를 설정해야 하는 매우 구체적인 사용 사례가 있습니다.이것이 내가 꽤 우아하다고 생각한 나의 해결책이었다.

const [value1, setValue1] = useState({value: 'whatever', onValue: false})


useEffect(() => {
    setValue1(prev => ({
      value: 'whatever',
      onValue: !prev.onValue, 
    }));
}, ['whatever'])

 
useEffect(() => {

// if you want to ensure the render happens before doThing2() then put it in a timeout of 1ms,
  setTimeout(doThing2, 1); 

// or if you are happy to call it immediately after setting value don't include the timeout
 doThing2()


}, [value1.onValue])

함수를 통과시키는 건 어때?

const [name, setName] = useState(initialName); 
...
setName(() => {
    const nextName = "Michael";
    console.log(nextName);
    return nextName;
  });

에게 은 ★★★★★★★★★★★★★★★★★★★★★★★★★★★」useState ★★★★★★★★★★★★★★★★★」useCallback:

코드 예시

import React, { useCallback, useState } from 'react';

const Test = () => {
  const [name, setName] = useState("");
  const testCallback = useCallback(() => console.log(name), [name]);

  return (
    <button onClick={() => {
      setName("Michael")
      testCallback();
    }}>Name</button>
  )
};

export default Test;

이거 어때:

const [Name, setName] = useState("");
...
onClick={()=>{
setName("Michael")
setName(prevName=>{...}) //prevName is Michael?
}}

UseEffect가 주요 솔루션입니다.그러나 Darryl이 설명한 바와 같이 useEffect를 사용하고 두 번째 파라미터로 상태를 전달하면 하나의 결함이 있습니다.이 컴포넌트는 초기화 프로세스에서 실행됩니다.콜백 함수가 갱신된 상태의 값을 사용하여 실행되도록 하는 경우 로컬 상수를 설정하고 setState와 콜백 모두에서 사용할 수 있습니다.

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

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

  // now you can "do something" with updatedNumber and don't have to worry about the async nature of setState!
  console.log(updatedNumber);
}

언급URL : https://stackoverflow.com/questions/56247433/how-to-use-setstate-callback-on-react-hooks

반응형