-
State, 리렌더링 조건카테고리 없음 2023. 5. 3. 17:12
State
<react 공식문서>
https://ko.legacy.reactjs.org/docs/faq-state.html
공식문서에서 state에 대해서 'setState()는 컴포넌트의 state 객체에 대한 업데이트를 실행합니다. state가 변경되면, 컴포넌트는 리렌더링됩니다.' 라고 나온다.
각각의 컴포넌트들은 서로 독립적으로 작동하며, 자신이 관리하는 상태 데이터를 가지고 있따. 이 상태 데이터를 관리하기 위해 리액트에서는 state라는 개념을 제공한다.
우리가 요새 주로 사용하는 함수형 컴포넌트에서는 react16.8버전 이후부터, 'useState'라는 Hook이 추가됬다.
'useState' Hook은 배열을 반환하며, 첫번째 요소는 현재 'state'값, 두 번째 요소는 'state'값을 변경하는 함수이다.
useState()괄호 안에 들어오는건 초기값이다.
import React, { useState } from 'react'; const [state, setState] = useState("")
<예시1 - count 만들기>
import "./App.css" import styled from "styled-components"; import { useState } from "react"; const StMain = styled.div` display:flex; align-items:center; flex-direction:column; font-size:30px; padding:40px; ` function App() { const [count, setCount] = useState(0) const onClickCountButtonHandle = (a) => { a === 'plus' ? setCount(count + 1) : setCount(count - 1) } return ( <StMain> 현재: {count} <div> <button onClick={() => onClickCountButtonHandle('plus')}>+</button> <button onClick={() => onClickCountButtonHandle('minus')}>-</button> </div> </StMain> ); } export default App;
위코드는 증가하거나 감소할때 기존 count값에 +1을 해줘서 setCount를 변화시켜서 count값을 변화주게했다. 초기값은 useState(0)으로 줘서 state값의 초기값을 0으로 설정했다.
이전에는 증가, 감소하는 버튼에 각각 다른 함수를 줘서 했었는데 오늘 문득 증가와 감소를 매개변수로 주고 함수에 삼항연산자로 조건식으로 함수를 실행시키는걸로 해봤는데
함수 실행시키는건 하나의 함수로 가능하지만 jsx부분에 onclick 부분이 좀 더 지저분해 보여서 어떤게 더 좋은 코드인지 의문이 들었다.
이건 내일 매니저님에게 질문해보고 다시 블로그 수정을 하던가 해야할 것 같다.
<예제2 input데이터>
import "./App.css" import styled from "styled-components"; import { useState } from "react"; const StMain = styled.div` display:flex; justify-content:center; align-items:center; flex-direction:column; font-size:30px; padding:40px; ` const Input = styled.input` margin:5px; padding:10px; ` function App() { const [myName, setMyName] = useState("") const [age, setAge] = useState(0) const onChangeNameHandle = (e) => { setMyName(e.target.value) } const onChangeAgeHandle = (e) => { setAge(e.target.value) } return ( <StMain> <div> 이름 : <Input value={myName} onChange={onChangeNameHandle} /> </div> <div> 나이 : <Input value={age} onChange={onChangeAgeHandle} /> </div> </StMain> ); } export default App;
위 코드에서 onChange가 같은 작업을 하는 걸 볼 수 있다.
const [state, setState] = useState({ myName: "", age: 0 }) const onChangeNameHandle = (e) => { setState(e.target.value) } const onChangeAgeHandle = (e) => { setState(e.target.value) }
state라는 useState를 만들고 myName과, age를 같이 관리 할 수 있다.
최종적으로 최대한 합치면
function App() { const [state, setState] = useState({ myName: "", age: 0 }) const onChangeHandle = (e) => { setState({ ...state, [e.target.name]: e.target.value }) } return ( <> <StMain> <div> 이름 : <Input name="myName" value={state.myName} onChange={onChangeHandle} /> </div> <div> 나이 : <Input name="age" value={state.age} onChange={onChangeHandle} /> </div> </StMain> </> ) }
이렇게 하나로 묶어서
const onChangeHandle = (e) => { setState({ ...state, [e.target.name]: e.target.value }) }
...state로 기존에 배열을 풀어주고 e.target.name으로 지금 현재 적고있는 위치의 value를 setState로 넣어서 state를 변화시킬 수 있다.
리렌더링 조건
렌더링이란?
리액트에서 렌더링이란, 컴포넌트가 현재 props와 state의 상태에 기초하여 UI를 어떻게 구성할지 컴포넌트에게 요청하는 작업을 의미한다.
함수형 컴포넌트 리렌더링의 조건
- Props가 변경되는 경우: 부모 컴포넌트에서 전달되는 Props가 변경되면, 함수형 컴포넌트는 Props의 변경을 감지하여 리렌더링된다.
- State가 변경되는 경우: 함수형 컴포넌트에서 useState Hook을 사용하여 상태를 관리하는 경우, 상태가 변경되면 컴포넌트는 리렌더링된다.
- Context가 변경되는 경우: 함수형 컴포넌트에서 useContext Hook을 사용하여 Context를 사용하는 경우, Context 값이 변경되면 컴포넌트가 리렌더링된다.
- useReducer Hook을 사용하여 상태를 관리하는 경우: useReducer Hook을 사용하여 상태를 관리하는 경우, 상태가 변경되면 컴포넌트가 리렌더링된다.
- 컴포넌트 자체적으로 관리하는 상태가 변경되는 경우: 함수형 컴포넌트에서 컴포넌트 자체적으로 상태를 관리하는 경우, 상태가 변경되면 컴포넌트가 리렌더링된다.
- forceUpdate() 함수가 호출되는 경우: 함수형 컴포넌트에서 forceUpdate() 함수를 호출하여 강제로 컴포넌트를 리렌더링할 수 있다.
- React.memo()를 사용하여 Props나 상태의 변경을 감지하지 않도록 설정하는 경우: React.memo() 함수를 사용하여 Props나 상태의 변경을 감지하지 않도록 설정할 수 있다. 이 경우, 해당 Props나 상태가 변경되어도 컴포넌트는 리렌더링되지 않는다.
리렌더링의 단점은 다음과 같다.
- 성능 저하: 리렌더링은 화면을 다시 그리는 작업을 수행하기 때문에 성능에 영향을 미친다. 특히, 컴포넌트의 수가 많아지면 리렌더링 시간이 길어질 수 있다.
- 불필요한 리렌더링: 컴포넌트의 Props나 State가 변경되지 않았더라도, 부모 컴포넌트나 Context의 값이 변경될 경우, 컴포넌트는 불필요하게 리렌더링된다. 이 경우, 성능이 저하될 뿐만 아니라, 컴포넌트의 자원도 낭비된다.
- 부작용 발생 가능성: 컴포넌트가 리렌더링될 때, useEffect Hook에서 등록한 부작용이 실행될 가능성이 있다. 이 때, 부작용이 의도하지 않은 결과를 초래할 수 있다.
- 컴포넌트 생명주기 메서드 호출: 컴포넌트가 리렌더링될 때마다, 생명주기 메서드가 호출된다. 이는 컴포넌트의 상태를 관리하는 데에만 집중하고 있지 않은 경우, 불필요한 작업을 수행할 가능성이 있다.
- 컴포넌트 구조 설계의 어려움: 리렌더링은 컴포넌트 구조 설계에도 영향을 미친다. 컴포넌트의 구조가 복잡해지면, 리렌더링을 최소화하는 것이 어려울 수 있다.
- 서버 사이드 렌더링의 어려움: 리액트에서 서버 사이드 렌더링을 수행할 때, 컴포넌트의 리렌더링은 성능 문제를 초래할 수 있다. 서버 사이드 렌더링을 수행할 때는, 초기 렌더링 이후에는 컴포넌트의 리렌더링을 최소화하는 것이 중요하다.
- forceUpdate() 함수가 호출되는 경우: 함수형 컴포넌트에서 forceUpdate() 함수를 호출하여 강제로 컴포넌트를 리렌더링할 수 있다.
- React.memo()를 사용하여 Props나 상태의 변경을 감지하지 않도록 설정하는 경우: React.memo() 함수를 사용하여 Props나 상태의 변경을 감지하지 않도록 설정할 수 있다. 이 경우, 해당 Props나 상태가 변경되어도 컴포넌트는 리렌더링되지 않는다.
useEffect Hook에서 등록한 부작용(Side Effect)이란, 컴포넌트의 렌더링 이후에 실행되는 작업을 의미한다. 대표적인 부작용으로는 API 호출, 이벤트 핸들러 등이 있다. useEffect Hook은 컴포넌트가 렌더링되고 난 이후에 매번 실행된다. 이 때, 등록된 부작용이 실행되는데, 이는 컴포넌트의 상태(State)나 Props가 변경될 때마다 매번 호출된다. 이를 통해, 컴포넌트의 상태에 따라 다양한 작업을 수행할 수 있다. 예를 들어, useEffect Hook을 사용하여 API 호출을 수행할 수 있다. 이 경우, 컴포넌트가 렌더링될 때마다 API 호출이 수행되므로, 화면이 다시 그려지는 데 시간이 걸릴 수 있다. 이를 최소화하기 위해서는, useEffect Hook의 두 번째 인자로 의존성 배열(Dependency Array)을 전달하여, 해당 의존성이 변경될 때만 부작용이 실행되도록 할 수 있다. 또한, useEffect Hook에서 등록한 부작용은 클린업(Cleanup) 함수를 반환할 수도 있다. 이를 통해, 컴포넌트가 언마운트(Unmount)될 때 등록된 부작용을 해제할 수 있다. 이는 메모리 누수(Memory Leak)를 방지하고, 불필요한 리소스 사용을 막는 데에 도움을 준다.