참조자료
1. https://stackoverflow.com/questions/42038590/when-to-use-react-setstate-callback
2. https://bamtory29.tistory.com/entry/React-useState%EC%9D%98-%EB%B9%84%EB%8F%99%EA%B8%B0%EC%A0%81-%EB%8F%99%EC%9E%91
3. https://velog.io/@kym123123/%EB%B9%84%EB%8F%99%EA%B8%B0%EB%A1%9C-%EB%8F%99%EC%9E%91%ED%95%98%EB%8A%94-react%EC%9D%98-setState%EC%97%90-%EB%8C%80%ED%95%98%EC%97%AC
const InputPrac = () => {
const [value, setValue] = useState(0); // value = 3 6 9 ...
const addNum = (e) => {
setValue(value + 1);
setValue(value + 2);
setValue(value + 3);
};
return (
<>
<p>{value}</p>
<button onClick={addNum}>클릭</button>
</>
);
};
export default InputPrac;
리액트는 setState가 호출되면 state가 변경되는 것으로 인지하는데,
한번의 이벤트 발생시 연속으로 같은 setstate가 실행되면,
한 state가 업데이트되어 연속으로 리렌더링 될 것이다.
이는 비효율적이고 비용도 많이 들 것이다.
때문에 이벤트 발생시 실행되는 콜백함수 내에서의 state의 "가장 마지막 갱신 내용"으로 state를 "한번만" 업데이트 시키게 된다.
const InputPrac = () => {
const [value, setValue] = useState(0); // value = 6 12 18 ...
const addNum = (e) => {
setValue((prev) => prev + 1); // prev = 0 // updated value = 1
setValue((prev) => prev + 2); // prev = 1 // updated value = 3
setValue((prev) => prev + 3); // prev = 3 // updated value = 6
};
return (
<>
<p>{value}</p>
<button onClick={addNum}>클릭</button>
</>
);
};
const InputPrac = () => {
const [value, setValue] = useState(0);
// value : 0 , 6 , 12 , 18
const [valueMul, setValueMul] = useState(100);
// valueMul : 100(value = 0) , 106(value = 6) , 118(value = 12)
const addNum = (e) => {
setValue((prev) => prev + 1); // 1
setValue((prev) => prev + 2); // 3
setValue((prev) => prev + 3); // 6, 12, 18
setValueMul((prev) => value + prev); // 100(value = 0) 106(value = 6) 118(value = 12)
};
return (
<>
<p>{value}</p>
<p>value가 바로 반영이 안됨 => {valueMul}</p>
<button onClick={addNum}>클릭</button>
</>
);
};
const InputPrac = () => {
const [inputValue, setInputValue] = useState("");
const [nameList, setNameList] = useState(names);
const onInput = (e) => {
// 1. 첫번째 setState에서 업데이트 한 state를 이용해
setInputValue(e.target.value);
// 2. 두번째 setState의 콜백함수에서 사용하려 하였음.
setNameList(() => {
return names.filter((el) => el.name.includes(inputValue));
});
};
return (
<>
<input type="text" onInput={onInput} />
<div id="searchId">
{nameList.map((el, idx) => {
return (
<div className="idBox" key={idx}>
<div className="idBoxRight">
<p>{el.name}</p>
<p>{el.korean}</p>
</div>
</div>
);
})}
</div>
</>
);
};
input창에 해당 글자 입력 하자마자 filter된 값이 렌더되야 하는데, 글자를 입력후 지워야 렌더가 됨.
Input이벤트가 발행하면 두개의 setState함수가 실행됨.
첫번째 setState는 state를 e.target.value값으로 업데이트 하고,
두번째 setState 콜백에선 그 업데이트 된 값을 이용해 해당 값을 갖고있는 요소만 filter하여 state를 업데이트하려고 했음.
그러나 e.target.value가 이전 값인 ""로 나오고, 다음 Input이벤트가 발생시 반영되는 식으로 밀려서 나옴.
하나의 이벤트에는 하나의 기능만 하는 함수를 걸어줘야 함
setState는 비동적으로 동작해서
setInputValue의 리턴값이 있다고 하더라도, 바로 state가 업데이트되어 리렌더링 되는게 아니라, onInput 이벤트 본문이 다 끝나야지 state가 업데이트 되는거여서!
setState 실행 후 바뀐 state값을 리액트가 갖고 있다가, onInput의 본문이 다 끝난 후 업데이트 시켜주는 것임.
따라서 안에서 setInputValue를 통해 inputValue를 수정한 후, 그 수정된 값을 사용하는 setNameList함수를 호출했다면, inputValue는 아직 바뀌지 않은 상태이기 때문에 이전 값인 ""를 사용하여 setNameList함수가 호출되고 있는것임.
input창의 입력값을 state로 받아서 처리했는데, 굳이 state로 만들 필요 없이, 그냥 변수로 만들어 사용하여 해결함.
const onInput = (e) => {
// 1. 인풋창에 입력된 값을 변수로 바로 받음
const searchValue = e.target.value;
// 2. 두번째 setState의 콜백함수에서 사용하려 하였음.
setNameList(() => {
return names.filter((el) => el.name.includes(searchValue));
});