본문 바로가기

React

[React] useState()

1.useState가 필요한 이유

function App() {
	let content = 'init content'
    
    function handleSelect(){
    	content = 'update content'
    }
    
    return {
    	<div>
        	<p>{content}</p>
        	<button onClick={handleSelect}>
        </div>
    
    
    }



}

1).잘못된 구현 : content의 값이 화면에서 업데이트 되지 않음. 즉 return()내의 jsx 코드가 한번만 실행되어 UI가 업데이트 되지 않음 > 모든 JSX코드가 재평가되지 않음

2). 값이 업데이트 되지 않는 이유 : 리액트는 JSX 코드를 보고 현재 렌더링된 UI와 비교하기 때문에 UI를 업데이트 하려면 리액트에 의해 재평가 되어야 함. 

 

3). 해결방안 : useState(); 

(1). 리액트 라이브러리에서 불러와야하는 특별한 함수의 도움을 받음

(2). 리액트에게 데이터가 변한것을 알려주고 리액트가 UI를 업데이트하는 리액트의 Hooks

*Hooks 함수는 컴포넌트 함수 안에서 바로 호출 되어야 하고 내부 함수 안에서 중첩되면 안됨 , 컴포넌트 함수의 최상위에서 호출되어야 함

import {useState} from 'react';

function App() {

	const [content, setContent] = useState('init content');
    
    
    function handleSelect(){
    	setContent('update content')
        console.log(content)
    }
    
    return {
    	<div>
        	<p>{content}</p>
        	<button onClick={handleSelect}>
        </div>
    
    
    }



}

(3). 코드 설명

content - 현재 상태값

setContent - 상태값 업데이트 함수 

useState('init content') - 초기 상태값

const를 사용하는 이유 : 값이 바뀌게 되면 리액트가 값을 저장하고 컴포넌트가 리렌더링 되면서 상수에 값이 전달 됨. 그렇기 때문에 값을 바꿀 이유가 없음.

코드 실행 과정 : 사용자가 버튼을 클릭 > handleSelect가 실행됨 > setContent를 통해 리액트는 상태 업데이트의 스케줄을 조정함 > console.log(content)에는 아직 값이 바뀌지 않은 'init content'가 찍힘 > handleSelect함수가 끝남 > 리액트가 값을 업데이트하고 컴포넌트를 리렌더링 함 > content 값은 'update content'로 변경됨

 

(4). setContent(content + 'update') 와 setContent((prev) => prev + 'update')는 뭐가 다를까 

setContent(content + 'update') 

import {useState} from 'react';

function App() {

	const [content, setContent] = useState('init content');
    
    
    function handleSelect(){
    	setContent(content + ' update'); //'init content update'로 상태값 변경 스케쥴 예약
        setContent(content + ' update'); //'init content update'로 상태값 변경 스케쥴 예약
        
    }
    
    return {
    	<div>
        	<p>{content}</p>
        	<button onClick={handleSelect}>
        </div>
    
    
    }



}

 

=> 위의 경우에는 <p>태그 내에 "init content update"가 나올것이다. setContent코드를 두번 쓰긴 했지만 저 코드의 의미는 content+ ' update'로 content상태값을 변경하는 스케줄예약을 두번 하는 것이다. 말그대로 예약이므로 content는 handleSelect내에서 변경되는것이 아니다. 즉 두번째 setContent가 실행될 때에도 content는 그대로 'init content'상태이기 때문에. 결과적으로 init content update가 찍히게 된다.

 

setContent((prev) => prev + 'update')

import {useState} from 'react';

function App() {

	const [content, setContent] = useState('init content');
    
    
    function handleSelect(){
    	setContent((prev) => prev + ' update') // 'init content update'로 상태값 변경 예약
        setContent((prev) => prev + 'update')  // 'init content update update'로 상태값 변경 예약
        
    }
    
    return {
    	<div>
        	<p>{content}</p>
        	<button onClick={handleSelect}>
        </div>
    
    
    }



}

=> 위의 경우에는 예정된 업데이트가 실행될 시점의 최신 상태값을 받음

 

태그 내에 "init content update update"가 나올것이다.함수식으로 사용하게 되면 실시간으로 최신값을 가져올 수 있다. setContent 내에 받아오는 prev 라는 매개변수는 가장 최신의 상태값을 가져오게 된다. 첫번째로 setContent를 쓰게 될 때 init content update로 바꿨고 두번째로 사용될 때는 리액트가 가장 최신 버전의 상태값을 가져오기 때문에 가장 최신의 상태값인 init content update + update가 되는것이다.

 

(5). 만약 상태값이 배열이나 객체인 경우

잘못된 접근

const [array, setArray] = useState([]);

function updateArray(idx,newVal){
	setArray((prev) => {
    	prev[idx] = newVal;
    	return prev;
    })

}

=> 배열이나 객체는 참조값이라서 메모리에 보관됨. 메모리 속의 기존값을 바로 변경하게 됨 => 리액트가 실행하는 예정된 상태 업데이트 보다 이전에 일어남 

 

올바른 접근

const [array, setArray] = useState([]);

function updateArray(idx,newVal){
	setArray((prev) => {
    	let newArray = [...prev]
    	newArray[idx] =newVal;
    	return newArray;
    })

}

=> 복사값을 만들어 업데이트 후 복사값 리턴

 

 

 

2. 상태값 배칭

1). 같은 state 업데이트가 여러번 있을 때 여러번 렌더링되지 않음 > 한번의 컴포넌트 함수 실행 유도