개발새발 로그

React - useState에 대하여 본문

React

React - useState에 대하여

이즈흐 2023. 12. 11. 17:25

useState

상태

컴포넌트의 상태를 간단하게 생성하고 업데이트 할 수 있게 해준다.

const [state, setState] = useState(initialState);

 

setState를 사용해서 state을 변경하면 다시 렌더링하게 된다.

그래서 컴포넌트 안의 함수와 변수가 다시 호출된다.

 

1. 초기값을 저장할 때 주의할 점!

그럼 만약 useState로 만든 state값의 초기 값에 무거운 작업을 해서 값을 저장하게 된다면

컴포넌트가 계속해서 렌더링될 때 성능에 굉장히 안좋을 수 있다.

 

아래 예를 보자

const heavyWokr =()=>{
   console.log("엄청 무거운 작업!!!")
   retrun ['홍길동','김민수']
}

const App=()=>{
	const [names,setNames] = useState(heavyWork());
    const [input,setInput] =useState('');
    
    const handleInputChange = (e) =>{
    	setInput(e.target.value);
    }
    
    const handleUpload = () =>{
    	setNames((prev)=>{
        	return [input,...prev]
        });
    }
    
    return (
    	<div>
        	<input type='text' onChange={handleInputChange}/>
            <button onClick={handleUpload}>Upload</button>
            {names.map((name,idx)=>{
            	return <p key={idx}>name<p>
            })}
        </div>
    )

}

그저 input 요소에 값을 입력해서 names 에 해당 값을 저장하고 출력해주는 로직이다.

근데 여기서 만약 input 요소에 값을 입력하는 행동을 한다면 아래와 같이 콘솔에 타이핑 수만큼 함수가 출력될 것이다.

이는 

input요소에 타이핑을 할 때

컴포넌트가 첫 렌더링을 할 때

Upload버튼을 클릭했을 때

모두 heavyWorK함수가 실행 된다.

 

정말 비효율적이다.

 

이를 해결하기 위해서는 heavyWork함수를 처음에 렌더링이 될 때만 불려지게 하는 것이다.

아래와 같이 콜백함수로 넣어주게 되면 맨 처음화면을 렌더링할 때만 불려지게 된다.

const [names,setNames] = useState(()=>{
    return heavyWork();
});

 

아래와 같이 초기화 함수로 전달할 수 있다.

// 이렇게 하면 안돼요!
function TodoList() {
 const [todos, setTodos] = useState(createInitialTodos());
 // ...
}


// 초기화 함수를 사용하세요
function TodoList() {
 const [todos, setTodos] = useState(createInitialTodos);
 // ...
}

 

함수를 호출한 결과인 createInitialTodos()가 아니라 함수 자체인 createInitialTodos를 전달하고 있다는 것을 봐야한다.

함수를 useState에 전달하면 React는 초기화 중에만 함수를 호출한다.

React는 이니셜라이저가 순수한지 확인하기 위해 개발 과정에서 이니셜라이저를 두 번 호출할 수도 있다.

 

2. setState시 주의할 점!

set 함수를 호출해도 이미 실행 중인 코드의 현재 상태는변경되지 않습니다

function handleClick() {
   setName('Robin');
   console.log(name); // 여전히 "테일러"!
}

다음 렌더링부터 반환되는 useState에만 영향을 줍니다.

 

 

3. setState는 이전상태를 기준으로 업데이트한다!

나이가 42세라고 가정합니다. 이 핸들러는 setAge(age + 1 )를 세 번 호출합니다

function handleClick() {
setAge(age + 1); // setAge(42 + 1)
setAge(age + 1); // setAge(42 + 1)
setAge(age + 1); // setAge(42 + 1)
}

하지만 한 번만 클릭하면 나이는 45세가 아니라 43세가 됩니다! 이는 set 함수를 호출해도 이미 실행 중인 코드에서 나이 상태 변수가 업데이트되지 않기 때문입니다. 따라서 각 setAge(age + 1) 호출은 setAge(43)이 됩니다.

 

 

이 문제를 해결하려면 다음 상태 대신 setAge에 업데이터 함수를 전달할 수 있습니다:

function handleClick() {
setAge(a => a + 1); // setAge(42 => 43)
setAge(a => a + 1); // setAge(43 => 44)
setAge(a => a + 1); // setAge(44 => 45)
}

여기서 a => a + 1은 업데이터 함수입니다. 이 함수는 보류 중인 상태를 가져와서 다음 상태를 계산합니다.

React는 업데이터 함수를 대기열에 넣습니다. 그런 다음 다음 렌더링 중에 동일한 순서로 호출합니다:

  1. a => a + 1은 보류 중인 상태로 42를 수신하고 다음 상태로 43을 반환합니다.
  2. a => a + 1은 보류 중인 상태로 43을 수신하고 다음 상태로 44를 반환합니다.
  3. a => a + 1은 보류 중인 상태로 44를 수신하고 다음 상태로 45를 반환합니다.

대기 중인 다른 업데이트가 없으므로 React는 결국 45를 현재 상태로 저장합니다.

일반적으로 상태 변수 이름의 첫 글자를 보류 중인 상태 인수의 이름으로 지정하는 것이 일반적입니다(예: for age) . 하지만 더 명확하다고 생각되는 다른 이름을 prevAge 등으로 지을 수도 있습니다.

 

React는 개발 과정에서 업데이트가 순수한지 확인하기 위해 업데이트자를 두 번 호출할 수 있습니다.

-> 컴포넌트 순수성 유지

리액트의 "순수성"은 컴포넌트가 주어진 입력에 대해 동일한 출력을 생성하는 것을 의미한다.

즉, 동일한 프로퍼티와 상태에 대해 항상 동일한 결과를 렌더링하는 것입니다.

 

 

4. state는 객체도 될 수 있다!

상태의 개체 및 배열 업데이트 

객체와 배열을 state에 넣을 수 있습니다.

React에서 state는 읽기 전용으로 간주되므로 기존 객체를 변경하지 말고 대체해야 합니다.

// 🚩 이런 상태의 개체는 변경하지 마세요:
form.firstName = 'Taylor';

대신 새 개체를 생성하여 전체 개체를 교체한다.

// ✅ state를 새 객체로 바꾸기
setForm({
...양식,
이름: 'Taylor'
});

 

아래와 같이 사용한다.

const [form, setForm] = useState({
    firstName: 'Barbara',
    lastName: 'Hepworth',
    email: 'bhepworth@sculpture.com',
  });
  
  ...
  
  <input
      value={form.lastName}
      onChange={e => {
        setForm({
          ...form,
          lastName: e.target.value
        });
      }}
    />
  
  ...

 

 

5. key로 상태 재설정하기 

map을 사용할 때 key 속성을 자주 접하게 된다.

하지만 key속성을 이용해서 다른 기능이 사용가능하다.

 

다른 컴포넌트에 key값을 전달하고, 

해당 key값을 변경하면 

React는 해당 컴포넌트를 처음부터 다시 생성하므로 상태가 초기화된다.

import { useState } from 'react';

export default function App() {
  const [version, setVersion] = useState(0);

  function handleReset() {
    setVersion(version + 1);
  }

  return (
    <>
      <button onClick={handleReset}>Reset</button>
      <Form key={version} />
    </>
  );
}

function Form() {
  const [name, setName] = useState('Taylor');

  return (
    <>
      <input
        value={name}
        onChange={e => setName(e.target.value)}
      />
      <p>Hello, {name}.</p>
    </>
  );
}

위 코드는 reset버튼이 있고,

Form 컴포넌트에서는 데이터 입력과 출력을 하고 있다.

이때 input의 값은 Taylor로 초기화되어있어 처음 렌더링 했을 때 Taylor을 나타나게 된다.

 

input의 값을 타이핑해서 변경하다가 reset버튼을 누르면 

다시 Taylor로 값이 바뀌게 된다.

 

이는 Form에 넘겨지는 key값이 변경되었고,

React에서는 해당 컴포넌트를 처음부터 다시 생성한다.

그래서 Taylor로 초기화 된 것이다!

728x90
반응형
LIST