반응 기능 구성 요소에서 setState의 기능 구문을 사용하는 것은 무엇입니까? [복제]
우리는 기능적 구성 요소에 대해 이야기하고 있습니다 useState
의 말을하자
const [age, setAge] = useState(0)
이제 업데이트하는 동안 age
이전을 사용해야합니다.age
React 문서 는 함수를 전달할 수있는 FUNCTIONAL UPDATES 라는 것을 언급하고 그에 대한 인수는 상태의 이전 값이 될 것입니다.
setState((previousAge) => previousAge + 1)
그냥 할 수 있는데 왜 이렇게해야하나요
setState(previousAge + 1)
기능 사용의 이점은 무엇인가 setState
,
클래스 기반 구성 요소에는 기능적 방식으로 상태 업데이트 일괄 처리 라는 것이 있다는 것을 알고 있지만 기능 구성 요소 문서에서는 이와 같은 것을 찾을 수 없습니다.
답변
업데이트가 상태에서 발견 된 이전 값에 따라 달라지는 경우에는 동일하지 않은 경우 기능적 형식을 사용해야합니다. 이 경우 함수형을 사용하지 않으면 언젠가 코드가 깨질 것입니다.
왜 깨지고 언제
React 기능적 구성 요소는 단지 클로저 일뿐입니다. 클로저에있는 상태 값이 구식 일 수 있습니다. 이것은 클로저 내부의 값이 해당 구성 요소에 대한 React 상태의 값과 일치하지 않는다는 것을 의미합니다. 다음과 같은 경우 :
1- 비동기 작업 ( 이 예제에서는 느린 추가를 클릭 한 다음 추가 버튼을 여러 번 클릭하면 나중에 느린 추가 버튼을 클릭했을 때 상태가 클로저 내부의 상태로 재설정되었음을 알 수 있습니다)
const App = () => {
const [counter, setCounter] = useState(0);
return (
<>
<p>counter {counter} </p>
<button
onClick={() => {
setCounter(counter + 1);
}}
>
immediately add
</button>
<button
onClick={() => {
setTimeout(() => setCounter(counter + 1), 1000);
}}
>
Add
</button>
</>
);
};
2- 동일한 클로저에서 업데이트 함수를 여러 번 호출 할 때
const App = () => {
const [counter, setCounter] = useState(0);
return (
<>
<p>counter {counter} </p>
<button
onClick={() => {
setCounter(counter + 1);
setCounter(counter + 1);
}}
>
Add twice
</button>
</>
);
}
setter가 얼마나 빨리 / 자주 호출되는지에 따라 문제가 발생할 수 있습니다.
클로저에서 값을 가져 오는 간단한 방법을 사용하는 경우 두 렌더링 간의 후속 호출이 원하는 효과를 얻지 못할 수 있습니다.
간단한 예 :
function App() {
const [counter, setCounter] = useState(0);
const incWithClosure = () => {
setCounter(counter + 1);
};
const incWithUpdate = () => {
setCounter(oldCounter => oldCounter + 1);
};
return (<>
<button onClick={_ => { incWithClosure(); incWithClosure(); }}>
Increment twice using incWithClosure
</button>
<button onClick={_ => { incWithUpdate(); incWithUpdate(); }}>
Increment twice using incWithUpdate
</button>
<p>{counter}</p>
</>);
}
두 버튼 모두 증분 메서드 중 하나를 두 번 호출합니다. 그러나 우리는 다음을 관찰합니다.
- 첫 번째 버튼은 카운터를 1 씩만 증가시킵니다.
- 두 번째 버튼은 카운터를 2 씩 증가시킬 것이며 이는 아마도 원하는 결과 일 것입니다.
언제 이런 일이 일어날 수 있습니까?
- 당연히,
incWithClosure
서로 즉시 여러 번 호출 되면 - 비동기 작업이 관련되면 쉽게 발생할 수 있습니다 (아래 참조).
- 아마도 React가 할 일이 많으면 스케줄링 알고리즘이 동일한 이벤트 핸들러를 사용하여 여러 번의 매우 빠른 클릭을 처리하도록 결정할 수 있습니다.
비동기 작업의 예 (리소스로드 시뮬레이션) :
function App() {
const [counter, setCounter] = useState(0);
const incWithClosureDelayed = () => {
setTimeout(() => {
setCounter(counter + 1);
}, 1000);
};
const incWithUpdateDelayed = () => {
setTimeout(() => {
setCounter((oldCounter) => oldCounter + 1);
}, 1000);
};
return (
<>
<button onClick={(_) => incWithClosureDelayed()}>
Increment slowly using incWithClosure
</button>
<button onClick={(_) => incWithUpdateDelayed()}>
Increment slowly using incWithUpdate
</button>
<p>{counter}</p>
</>
);
}
첫 번째 버튼을 두 번 (1 초 이내) 클릭하고 카운터가 1만큼만 증가하는지 확인합니다. 두 번째 버튼은 올바른 동작을합니다.
당신이하지 않으면 때문에 것이다 당신을위한 이전 값을 얻을 어떤 점에서 찾을 수 있습니다 age
. 문제는 때때로 당신이 제안한 것이 효과가 있다는 것입니다. 그러나 때로는 그렇지 않습니다. 현재 현재 코드에서 손상되지 않을 수 있지만 몇 주 전에 작성한 다른 코드 또는 지금부터 몇 달 후 현재 코드에서 손상 될 수 있습니다.
증상은 정말, 정말 이상합니다. {x}
구문을 사용하여 jsx 구성 요소 내부의 변수 값을 인쇄 하고 나중에 jsx 구성 요소를 렌더링 한 console.log
후 (이전이 아님)을 사용하여 동일한 변수를 인쇄 할 수 있으며 console.log
값이 부실 console.log
하다는 것을 알 수 있습니다. 렌더링 후 발생 하는 값은 어떻게 든 이전 값을 가질 수 있습니다. 렌더링보다.
따라서 상태 변수의 실제 값은 일반 코드에서 항상 올바르게 작동하지 않을 수 있습니다. 렌더링에서 최신 값을 반환하도록 설계되었습니다. 이러한 이유로 상태 설정 기의 콜백 메커니즘은 렌더링 외부의 일반 코드에서 상태 변수의 최신 값을 가져올 수 있도록 구현되었습니다.