리액트 훅을 사용한 상태 갱신 시 비동기 코드 실행
다음과 같은 것이 있습니다.
const [loading, setLoading] = useState(false);
...
setLoading(true);
doSomething(); // <--- when here, loading is still false.
상태 설정은 아직 비동기이므로 기다리는 가장 좋은 방법은 무엇입니까?setLoading()
완료하기 위해 콜?
그setLoading()
같은 콜백을 받아들이지 않는 것 같습니다.setState()
하곤 했다.
예
클래스 베이스의
getNextPage = () => {
// This will scroll back to the top, and also trigger the prefetch for the next page on the way up.
goToTop();
if (this.state.pagesSeen.includes(this.state.page + 1)) {
return this.setState({
page: this.state.page + 1,
});
}
if (this.state.prefetchedOrders) {
const allOrders = this.state.orders.concat(this.state.prefetchedOrders);
return this.setState({
orders: allOrders,
page: this.state.page + 1,
pagesSeen: [...this.state.pagesSeen, this.state.page + 1],
prefetchedOrders: null,
});
}
this.setState(
{
isLoading: true,
},
() => {
getOrders({
page: this.state.page + 1,
query: this.state.query,
held: this.state.holdMode,
statuses: filterMap[this.state.filterBy],
})
.then((o) => {
const { orders } = o.data;
const allOrders = this.state.orders.concat(orders);
this.setState({
orders: allOrders,
isLoading: false,
page: this.state.page + 1,
pagesSeen: [...this.state.pagesSeen, this.state.page + 1],
// Just in case we're in the middle of a prefetch.
prefetchedOrders: null,
});
})
.catch(e => console.error(e.message));
},
);
};
기능 베이스로 변환하다
const getNextPage = () => {
// This will scroll back to the top, and also trigger the prefetch for the next page on the way up.
goToTop();
if (pagesSeen.includes(page + 1)) {
return setPage(page + 1);
}
if (prefetchedOrders) {
const allOrders = orders.concat(prefetchedOrders);
setOrders(allOrders);
setPage(page + 1);
setPagesSeen([...pagesSeen, page + 1]);
setPrefetchedOrders(null);
return;
}
setIsLoading(true);
getOrders({
page: page + 1,
query: localQuery,
held: localHoldMode,
statuses: filterMap[filterBy],
})
.then((o) => {
const { orders: fetchedOrders } = o.data;
const allOrders = orders.concat(fetchedOrders);
setOrders(allOrders);
setPage(page + 1);
setPagesSeen([...pagesSeen, page + 1]);
setPrefetchedOrders(null);
setIsLoading(false);
})
.catch(e => console.error(e.message));
};
위에서는 각 setWhy 콜을 순차적으로 실행하려고 합니다.즉, 이 동작을 복제하려면 다양한 useEffect 훅을 설정해야 합니까?
useState
setter는 React 클래스 컴포넌트에서 setState와 같이 상태 갱신이 완료된 후 콜백을 제공하지 않습니다.동일한 동작을 복제하기 위해 다음과 같은 유사한 패턴을 사용할 수 있습니다.componentDidUpdate
React 클래스 컴포넌트의 라이프 사이클 메서드useEffect
후크 사용
useEffect
hooks는 렌더링 사이클이 완료된 후 React가 변경을 모니터링해야 하는 값의 배열로 두 번째 파라미터를 사용합니다.
const [loading, setLoading] = useState(false);
...
useEffect(() => {
doSomething(); // This is be executed when `loading` state changes
}, [loading])
setLoading(true);
편집
와는 달리setState
, 의 업데이트 프로그램useState
훅에는 콜백이 없습니다만, 항상 콜백을 사용할 수 있습니다.useEffect
위의 동작을 재현합니다.그러나 로드 변경 사항을 확인해야 합니다.
코드에 대한 기능적 접근법은 다음과 같습니다.
function usePrevious(value) {
const ref = useRef();
useEffect(() => {
ref.current = value;
});
return ref.current;
}
const prevLoading = usePrevious(isLoading);
useEffect(() => {
if (!prevLoading && isLoading) {
getOrders({
page: page + 1,
query: localQuery,
held: localHoldMode,
statuses: filterMap[filterBy],
})
.then((o) => {
const { orders: fetchedOrders } = o.data;
const allOrders = orders.concat(fetchedOrders);
setOrders(allOrders);
setPage(page + 1);
setPagesSeen([...pagesSeen, page + 1]);
setPrefetchedOrders(null);
setIsLoading(false);
})
.catch(e => console.error(e.message));
}
}, [isLoading, preFetchedOrders, orders, page, pagesSeen]);
const getNextPage = () => {
// This will scroll back to the top, and also trigger the prefetch for the next page on the way up.
goToTop();
if (pagesSeen.includes(page + 1)) {
return setPage(page + 1);
}
if (prefetchedOrders) {
const allOrders = orders.concat(prefetchedOrders);
setOrders(allOrders);
setPage(page + 1);
setPagesSeen([...pagesSeen, page + 1]);
setPrefetchedOrders(null);
return;
}
setIsLoading(true);
};
컴포넌트가 다시 렌더링될 때까지 기다립니다.
const [loading, setLoading] = useState(false);
useEffect(() => {
if (loading) {
doSomething();
}
}, [loading]);
setLoading(true);
다음과 같은 방법으로 선명도를 향상시킬 수 있습니다.
function doSomething() {
// your side effects
// return () => { }
}
function useEffectIf(condition, fn) {
useEffect(() => condition && fn(), [condition])
}
function App() {
const [loading, setLoading] = useState(false);
useEffectIf(loading, doSomething)
return (
<>
<div>{loading}</div>
<button onClick={() => setLoading(true)}>Click Me</button>
</>
);
}
커스텀을 작성useState
통상적인 것과 비슷한 후크useState
단, 이 커스텀훅의 상태 업데이터 함수는 상태가 갱신되고 컴포넌트가 재렌더된 후에 실행되는 콜백을 받습니다.
타이프스크립트 솔루션
import { useEffect, useRef, useState } from 'react';
type OnUpdateCallback<T> = (s: T) => void;
type SetStateUpdaterCallback<T> = (s: T) => T;
type SetStateAction<T> = (newState: T | SetStateUpdaterCallback<T>, callback?: OnUpdateCallback<T>) => void;
export function useCustomState<T>(init: T): [T, SetStateAction<T>];
export function useCustomState<T = undefined>(init?: T): [T | undefined, SetStateAction<T | undefined>];
export function useCustomState<T>(init: T): [T, SetStateAction<T>] {
const [state, setState] = useState<T>(init);
const cbRef = useRef<OnUpdateCallback<T>>();
const setCustomState: SetStateAction<T> = (newState, callback?): void => {
cbRef.current = callback;
setState(newState);
};
useEffect(() => {
if (cbRef.current) {
cbRef.current(state);
}
cbRef.current = undefined;
}, [state]);
return [state, setCustomState];
}
Javascript 솔루션
import { useEffect, useRef, useState } from 'react';
export function useCustomState(init) {
const [state, setState] = useState(init);
const cbRef = useRef();
const setCustomState = (newState, callback) => {
cbRef.current = callback;
setState(newState);
};
useEffect(() => {
if (cbRef.current) {
cbRef.current(state);
}
cbRef.current = undefined;
}, [state]);
return [state, setCustomState];
}
사용.
const [state, setState] = useCustomState(myInitialValue);
...
setState(myNewValueOrStateUpdaterCallback, () => {
// Function called after state update and component rerender
})
비동기 상태 후크를 작성할 수 있습니다.
const useAsyncState = initialState => {
const [state, setState] = useState(initialState);
const asyncSetState = value => {
return new Promise(resolve => {
setState(value);
setState((current) => {
resolve(current);
return current;
});
});
};
return [state, asyncSetState];
};
그리고나서
const [loading, setLoading] = useAsyncState(false)
const submit = async () => {
await setLoading(true)
dosomething()
}
이것에 대한 제안이 있습니다.
React Ref를 사용하여 상태 변수의 상태를 저장할 수 있습니다.그런 다음 상태 변수를 react ref로 업데이트합니다.그러면 페이지 새로 고침이 렌더링되고 비동기 함수로 React Ref가 사용됩니다.
const stateRef = React.useRef().current
const [state,setState] = useState(stateRef);
async function some() {
stateRef = { some: 'value' }
setState(stateRef) // Triggers re-render
await some2();
}
async function some2() {
await someHTTPFunctionCall(stateRef.some)
stateRef = null;
setState(stateRef) // Triggers re-render
}
값이 아닌 함수를 설정기에 전달!
세터에 직접 새 값을 제공하는 대신 현재 상태 값을 가져와서 새 값을 반환하는 화살표 함수를 전달합니다.
강제로 상태 업데이트를 연결하고 모든 업데이트가 완료된 후 컴포넌트를 다시 렌더링합니다.
const [counter, setCounter] = useState(0);
const incrementCount = () => {
setCounter( (counter) => { return counter + 1 } )
}
incrementCount가 호출될 때마다 카운트가 1씩 증가하고 1로 고정되지 않게 됩니다.
언급URL : https://stackoverflow.com/questions/53898810/executing-async-code-on-update-of-state-with-react-hooks
'programing' 카테고리의 다른 글
Spring REST 서비스: json 응답의 null 개체를 제거하도록 구성하는 방법 (0) | 2023.03.18 |
---|---|
스프링 임베디드 Kafka를 사용한 @Kafka Listener 테스트 (0) | 2023.03.18 |
참조 오류:Jest 환경이 해체된 후 파일을 '가져오기'하려고 합니다. (0) | 2023.03.18 |
Facebook의 앱 내 브라우저가 "net::ERR_FAILED"이지만 다른 브라우저는 없습니다. (0) | 2023.03.18 |
TypeError: db.collection이 함수가 아닙니다. (0) | 2023.03.18 |