컴포넌트의 useState에서 업데이터를 스테이트하기 위한 콜이 여러 번 발생하면 여러 번 재렌더가 발생합니다.
처음 React 훅을 시도하고 있는데 데이터를 가져와 두 개의 다른 상태 변수(데이터 및 로드 플래그)를 업데이트하면 두 개의 상태 업데이트 프로그램에 대한 호출이 모두 동일한 함수로 발생하더라도 구성 요소(데이터 테이블)가 두 번 렌더링된다는 것을 깨닫기 전까지는 모든 것이 정상으로 보였습니다.여기 두 변수를 컴포넌트로 되돌리는 API 함수가 있습니다.
const getData = url => {
const [data, setData] = useState(null);
const [loading, setLoading] = useState(true);
useEffect(async () => {
const test = await api.get('/people')
if(test.ok){
setLoading(false);
setData(test.data.results);
}
}, []);
return { data, loading };
};
일반 클래스 컴포넌트에서는 복잡한 오브젝트일 수 있는 상태를 갱신하기 위해1개의 콜을 발신합니다만, 「훅 방식」은, 상태를 작은 단위로 분할하는 것으로 보여집니다.이것의 부작용은, 개별적으로 갱신하면 복수의 재렌더인 것 같습니다.어떻게 줄일지 생각나는 거 없어?
'어울리다'를 해서 볼 수 요.loading
및 ""data
오브젝트로 , 1개의 스테이트 오브젝트를 할 수 .setState
이치노릇을 하다
주의: 다음 명령과 달리setState
컴포넌트에서의 '클래스 컴포넌트'setState
서서에서 useState
개체를 기존 상태와 병합하는 것이 아니라 개체를 완전히 대체합니다.병합을 수행할 경우 이전 상태를 읽고 직접 새 값과 병합해야 합니다.문서를 참조해 주세요.
퍼포먼스에 문제가 있다고 판단될 때까지 렌더링 콜에 대해 크게 걱정하지 않습니다.(React 컨텍스트에서) 렌더링과 실제 DOM에 대한 가상 DOM 업데이트 커밋은 다른 문제입니다.여기서 렌더링하는 것은 브라우저의 DOM 업데이트가 아니라 가상 DOM 생성을 의미합니다.는 ""를 .setState
를 호출하여 브라우저의 DOM을 최종 새로운 상태로 업데이트합니다.
const {useState, useEffect} = React;
function App() {
const [userRequest, setUserRequest] = useState({
loading: false,
user: null,
});
useEffect(() => {
// Note that this replaces the entire object and deletes user key!
setUserRequest({ loading: true });
fetch('https://randomuser.me/api/')
.then(results => results.json())
.then(data => {
setUserRequest({
loading: false,
user: data.results[0],
});
});
}, []);
const { loading, user } = userRequest;
return (
<div>
{loading && 'Loading...'}
{user && user.name.first}
</div>
);
}
ReactDOM.render(<App />, document.querySelector('#app'));
<script src="https://unpkg.com/react@16.7.0-alpha.0/umd/react.development.js"></script>
<script src="https://unpkg.com/react-dom@16.7.0-alpha.0/umd/react-dom.development.js"></script>
<div id="app"></div>
대안 - 주 합병 후크를 직접 작성합니다.
const {useState, useEffect} = React;
function useMergeState(initialState) {
const [state, setState] = useState(initialState);
const setMergedState = newState =>
setState(prevState => Object.assign({}, prevState, newState)
);
return [state, setMergedState];
}
function App() {
const [userRequest, setUserRequest] = useMergeState({
loading: false,
user: null,
});
useEffect(() => {
setUserRequest({ loading: true });
fetch('https://randomuser.me/api/')
.then(results => results.json())
.then(data => {
setUserRequest({
loading: false,
user: data.results[0],
});
});
}, []);
const { loading, user } = userRequest;
return (
<div>
{loading && 'Loading...'}
{user && user.name.first}
</div>
);
}
ReactDOM.render(<App />, document.querySelector('#app'));
<script src="https://unpkg.com/react@16.7.0-alpha.0/umd/react.development.js"></script>
<script src="https://unpkg.com/react-dom@16.7.0-alpha.0/umd/react-dom.development.js"></script>
<div id="app"></div>
도 또 다른 요.useReducer
setState
.
const [state, setState] = useReducer(
(state, newState) => ({...state, ...newState}),
{loading: true, data: null, something: ''}
)
에는 그냥 처럼 쓸 수요.this.setState
「」를 참조해 주세요.this
!
setState({loading: false, data: test.data.results})
★★★★★★★★★★★★★★★★★★★★★★★★★★★에서 알 수 있듯이,setState
)this.setState
모든 상태를 함께 업데이트할 필요는 없습니다.예를 들어, 다음과 같이 상태 중 하나를 변경할 수 있습니다(다른 상태에는 영향을 주지 않습니다.
setState({loading: false})
대박, 하?!
자, 그럼 모든 부품을 함께 정리해 봅시다.
import {useReducer} from 'react'
const getData = url => {
const [state, setState] = useReducer(
(state, newState) => ({...state, ...newState}),
{loading: true, data: null}
)
useEffect(async () => {
const test = await api.get('/people')
if(test.ok){
setState({loading: false, data: test.data.results})
}
}, [])
return state
}
타이프스크립트 지원.이 솔루션에 응답한 P. Galbraith 씨 덕분에 타이프 스크립트를 사용하는 사용자는 다음을 사용할 수 있습니다.
useReducer<Reducer<MyState, Partial<MyState>>>(...)
어디에MyState
는 상태 객체의 유형입니다.
예: 이 경우 다음과 같습니다.
interface MyState {
loading: boolean;
data: any;
something: string;
}
const [state, setState] = useReducer<Reducer<MyState, Partial<MyState>>>(
(state, newState) => ({...state, ...newState}),
{loading: true, data: null, something: ''}
)
이전 상태 지원코멘트에서는, 유저 2420374는, 다음의 코멘트에 액세스 할 수 있는 방법을 문의했습니다.prevState
우리 안에setState
이 목표를 달성하기 위한 방법은 다음과 같습니다.
const [state, setState] = useReducer(
(state, newState) => {
newWithPrevState = isFunction(newState) ? newState(state) : newState
return (
{...state, ...newWithPrevState}
)
},
initialState
)
// And then use it like this...
setState(prevState => {...})
isFunction
는 passed 인수가 함수(즉 prevState에 액세스하려고 하는 것을 의미)인지 플레인 오브젝트인지를 확인합니다.Alex Grande의 isFunction 구현은 여기에서 확인할 수 있습니다.
주목. 이 답을 많이 쓰시는 분들을 위해 도서관으로 바꾸기로 했습니다.다음 URL에서 찾을 수 있습니다.
Github: https://github.com/thevahidal/react-use-setstate
NPM : https://www.npmjs.com/package/react-use-setstate
리액트 훅에서의 배치 갱신 https://github.com/facebook/react/issues/14259
현재 React는 버튼 클릭이나 입력 변경과 같은 React 기반 이벤트 내에서 트리거되는 경우 상태 업데이트를 일괄 처리합니다.비동기 호출과 같이 React 이벤트 핸들러 외부에서 트리거된 업데이트는 배치되지 않습니다.
이것으로 끝납니다.
const [state, setState] = useState({ username: '', password: ''});
// later
setState({
...state,
username: 'John'
});
복제하려면this.setState
클래스 컴포넌트로부터의 머지 동작, React docs의 기능 형식 사용을 권장합니다.useState
오브젝트 확산 - 불필요useReducer
:
setState(prevState => {
return {...prevState, loading, data};
});
이제 두 상태가 하나로 통합되어 렌더링 주기를 절약할 수 있습니다.
하나의 상태 오브젝트에는 또 다른 장점이 있습니다.loading
그리고.data
종속된 상태입니다.무효 상태의 변경은, 상태를 정리하면 보다 명확하게 됩니다.
setState({ loading: true, data }); // ups... loading, but we already set data
1) 상태를 다음과 같이 함으로써 일관성 있는 상태를 더욱 확실하게 보장할 수 있습니다.loading
,success
,error
, 등 - 자신의 주(州)에 명시되어 있으며 2.를 사용하고 있습니다.useReducer
상태 로직을 리듀서에 캡슐화하려면:
const useData = () => {
const [state, dispatch] = useReducer(reducer, /*...*/);
useEffect(() => {
api.get('/people').then(test => {
if (test.ok) dispatch(["success", test.data.results]);
});
}, []);
};
const reducer = (state, [status, payload]) => {
if (status === "success") return { ...state, data: payload, status };
// keep state consistent, e.g. reset data, if loading
else if (status === "loading") return { ...state, data: undefined, status };
return state;
};
const App = () => {
const { data, status } = useData();
return status === "loading" ? <div> Loading... </div> : (
// success, display data
)
}
const useData = () => {
const [state, dispatch] = useReducer(reducer, {
data: undefined,
status: "loading"
});
useEffect(() => {
fetchData_fakeApi().then(test => {
if (test.ok) dispatch(["success", test.data.results]);
});
}, []);
return state;
};
const reducer = (state, [status, payload]) => {
if (status === "success") return { ...state, data: payload, status };
// e.g. make sure to reset data, when loading.
else if (status === "loading") return { ...state, data: undefined, status };
else return state;
};
const App = () => {
const { data, status } = useData();
const count = useRenderCount();
const countStr = `Re-rendered ${count.current} times`;
return status === "loading" ? (
<div> Loading (3 sec)... {countStr} </div>
) : (
<div>
Finished. Data: {JSON.stringify(data)}, {countStr}
</div>
);
}
//
// helpers
//
const useRenderCount = () => {
const renderCount = useRef(0);
useEffect(() => {
renderCount.current += 1;
});
return renderCount;
};
const fetchData_fakeApi = () =>
new Promise(resolve =>
setTimeout(() => resolve({ ok: true, data: { results: [1, 2, 3] } }), 3000)
);
ReactDOM.render(<App />, document.getElementById("root"));
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.13.0/umd/react.production.min.js" integrity="sha256-32Gmw5rBDXyMjg/73FgpukoTZdMrxuYW7tj8adbN8z4=" crossorigin="anonymous"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.13.0/umd/react-dom.production.min.js" integrity="sha256-bjQ42ac3EN0GqK40pC9gGi/YixvKyZ24qMP/9HiGW7w=" crossorigin="anonymous"></script>
<div id="root"></div>
<script>var { useReducer, useEffect, useState, useRef } = React</script>
PS: 커스텀 훅에 프리픽스를 붙이는 것을 확인합니다.use
)useData
getData
했습니다.useEffect
수 없다async
.
수 경우 를 사용할 수 없는 useReducer
해서 하면 됩니다.
ReactDOM.unstable_batchedUpdates(() => { ... })
댄 아브라모프 추천
이 예를 참조해 주세요.
https://stackoverflow.com/a/53575023/121143에 대한 답변에 대한 간단한 추가 정보
멋지다! 이 훅을 사용하려는 사용자에게는 다음과 같이 함수를 인수로 사용할 수 있는 약간 견고한 방법으로 쓸 수 있다.
const useMergedState = initial => {
const [state, setState] = React.useState(initial);
const setMergedState = newState =>
typeof newState == "function"
? setState(prevState => ({ ...prevState, ...newState(prevState) }))
: setState(prevState => ({ ...prevState, ...newState }));
return [state, setMergedState];
};
업데이트: 최적화된 버전. 들어오는 부분 상태가 변경되지 않은 경우 상태가 수정되지 않습니다.
const shallowPartialCompare = (obj, partialObj) =>
Object.keys(partialObj).every(
key =>
obj.hasOwnProperty(key) &&
obj[key] === partialObj[key]
);
const useMergedState = initial => {
const [state, setState] = React.useState(initial);
const setMergedState = newIncomingState =>
setState(prevState => {
const newState =
typeof newIncomingState == "function"
? newIncomingState(prevState)
: newIncomingState;
return shallowPartialCompare(prevState, newState)
? prevState
: { ...prevState, ...newState };
});
return [state, setMergedState];
};
Yangshun Tay의 대답 외에 메모하는 것이 좋습니다.setMergedState
따라서 새 함수 대신 각 렌더에 동일한 참조가 반환됩니다.는 TypeScript 가 TypeScript linter를 매우 합니다.setMergedState
의 useCallback
★★★★★★★★★★★★★★★★★」useEffect
부모 컴포넌트에 있습니다.
import {useCallback, useState} from "react";
export const useMergeState = <T>(initialState: T): [T, (newState: Partial<T>) => void] => {
const [state, setState] = useState(initialState);
const setMergedState = useCallback((newState: Partial<T>) =>
setState(prevState => ({
...prevState,
...newState
})), [setState]);
return [state, setMergedState];
};
이 경우에도 하실 수 있습니다.useEffect
변화를 다른 값을 합니다.
언급URL : https://stackoverflow.com/questions/53574614/multiple-calls-to-state-updater-from-usestate-in-component-causes-multiple-re-re
'programing' 카테고리의 다른 글
mysql 경고 해결 방법: "InnoDB: page_cleaner: 1000ms 의도 루프에 XXXms가 걸렸습니다.설정이 최적이 아닐 수 있습니다. (0) | 2022.10.23 |
---|---|
SQL Syntax Error for 5.5.35-MariaDB (0) | 2022.10.23 |
Javascript 변수 유형을 얻는 더 좋은 방법? (0) | 2022.10.23 |
문자열로 표시되도록 전달된 JSON 열 - Larabel - Vuejs2 (0) | 2022.10.23 |
C의 등식을 위한 구조를 어떻게 비교합니까? (0) | 2022.10.23 |