1. React Hooks - useState
ⅰ. useState 복습
- useState는 가장 기본적인 hook이며, 함수 컴포넌트에서 가변적인 상태를 가지게 해줌
const [state, setState] = useState(initialState);
- state가 원시 데이터타입이 아닌 객체 데이터 타입인 경우 불변성을 유지해줘야함
ⅱ. 함수형 업데이터
- 함수형 업데이트란?
- useState를 사용하는 방식에는 함수형 업데이트 방식도 있음
- setState의 ()안에 수정할 값이 아니라, 함수를 넣을 수 있음
그 함수의 인자에서는 현재 state를 가져올 수 있고, {} 안에서는 이 값을 변경하는 코드를 작성할 수 있음
// 기존에 우리가 사용하던 방식
setState(number + 1);
// 함수형 업데이트
setState(() => {});
// 현재 number의 값을 가져와서 그 값에 +1을 더하여 반환한 것 입니다.
setState((currentNumber)=>{ return currentNumber + 1 });
- 차이점
// 일반 업데이트 방식
// src/App.js
import { useState } from "react";
const App = () => {
const [number, setNumber] = useState(0);
return (
<div>
{/* 버튼을 누르면 1씩 플러스된다. */}
<div>{number}</div>
<button
onClick={() => {
setNumber(number + 1); // 첫번째 줄
setNumber(number + 1); // 두번쨰 줄
setNumber(number + 1); // 세번째 줄
}}
>
버튼
</button>
</div>
);
}
export default App;
// 함수형 업데이트 방식
// src/App.js
import { useState } from "react";
const App = () => {
const [number, setNumber] = useState(0);
return (
<div>
{/* 버튼을 누르면 3씩 플러스 된다. */}
<div>{number}</div>
<button
onClick={() => {
setNumber((previousState) => previousState + 1);
setNumber((previousState) => previousState + 1);
setNumber((previousState) => previousState + 1);
}}
>
버튼
</button>
</div>
);
}
export default App;
- 일반 업데이트 방슥은 첫번째 ~ 세번째 줄에 있는 setNumber가 각각 실행되는 것이 아니라, 배치(batch)로 처리함 => 배치 업데이트
=> 리액트에서는 컴포넌트를 변경하는방법 즉, 렌더링을 하기 위해서 state를 파악하는데 그 state를 파악하는 방법은 배치 업데이트
배치 업데이트는 한꺼번에 변경된 내용들을 모아서 한번만 반영을 하기 때문에 setNumber을 3번을 해도 동일한 내용이므로 한개로 인식하여 한번만 처리됨 - 함수형 업데이트 방식은 3번을 동시에 명령을 내리지만, 그 명령을 모아 순차적으로 각각 1번씩 실행시킴
ⅲ. 리액트는 왜 배치 업데이트가 되도록 설계했을까?
- 리액트 환경에서 렌더링이 잦은건 좋은게 아님 => 성능에 이슈가 발생함
- 불필요한 렌더링을 피하기 위해서 한꺼번에 요청사항을 모아서 한번만 처리되는게 렌더링을 줄일 수 있는 좋은 방법
- 불필요한 리 렌더링을 방지(렌더링 최적화)하기 위해 즉, 리액트의 성능을 위해 한꺼번에 state 업데이트를 한다고함
리액트는 성능을 위해 setState()를 단일 업데이트(batch update)로 한꺼번에 처리할 수 있습니다.
useState의 업데이트 방식은 2가지 방식이 있으며, 각각 다르게 동작함
useState로 원시데이터가 아닌 데이터를 변경할때는 불변성을 유지해야함
2. useEffect
ⅰ. useEffect란?
- useEffect 사용
- useEffect는 리액트 컴포넌트가 렌더링될 때마다 특정 작업을 수행하도록 설정할 수 있는 리액트 Hook
- 컴포넌트가 화면에 보여졌을 때 내가 무언가를 실행하고 싶다면 또는 어떤 컴포넌트가 화면에서 사라졌을 때 무언가를 실행하고 싶다면 useEffect를 사용함
- useEffect의 기초
import React, { useEffect } from 'react'
const App = () => {
useEffect(() => {
console.log("hello useEffect!")
})
return (
<div>App</div>
)
}
export default App
📚 App 컴포넌트가 화면에 렌더링될 떼 useEffect 안에 있는 console.log 가 실행됨.
📍 컴포넌트가 렌더링 될 때 실행된다 이게 바로 useEffect 핵심기능!!!!
- useEffect와 리렌더링(re-rendering)
- useEffect는 useEffect가 속한 컴포넌트가 화면에 렌더링 될 때 실행되는데 이런 useEffect의 특징에 의해 의도치 않는 동작을 경험할 수 있음
import React, { useEffect, useState } from "react";
const App = () => {
const [value, setValue] = useState("");
useEffect(() => {
console.log("hello useEffect!");
});
return (
<div>
<input
type="text"
value={value}
onChange={(e) => {
setValue(e.target.value);
}}
/>
</div>
);
};
export default App;
📚 input에 어떤 값을 입력하면 useEffect가 계속 실행됨
전체 흐름
1. input 에 값을 입력
2 . value, 즉 state가 변경됨
3. state가 바뀌었기 때문에 => App컴포넌트가 리렌더링됨
4. 리렌더링이 됨 => useEffect가 다시 실행됨
1 - 4 지속적으로 반복됨 => 이러한 것을 해결하기 위해서 의존성 배열이라는 것이 필요함
ⅱ. 의존성 배열(dependency array)
- 의존성 배열이란?
- useEffect에는 의존성 배열이라는 것이 있음.
- 이 배열에 값을 넣으면 그 값이 바뀔 때만 useEffect를 실행한다!
// useEffect의 두번째 인자가 의존성 배열이 들어가는 곳 입니다.
useEffect(()=>{
// 실행하고 싶은 함수
}, [의존성배열])
- 의존성 배열이 빈 배열인 경우
- 의존성 배열안에는 어떠한 값을 넣지 않았으니 useEffect는 처음에 딱 한번만 실행되고 그 이후로는 어떤일이 일어나도 실행이 되서는 안됨
- 어떠한 함수를 컴포넌트가 렌더링 될 때 단 한번만 실행하고 싶으면 의존성 배열을 [] 빈 상태로 넣으면 됨
// src/App.js
import React, { useEffect, useState } from "react";
const App = () => {
const [value, setValue] = useState("");
useEffect(() => {
console.log("hello useEffect");
}, []); // 비어있는 의존성 배열
return (
<div>
<input
type="text"
value={value}
onChange={(event) => {
setValue(event.target.value);
}}
/>
</div>
);
}
export default App;
- 의존성 배열에 값이 있는 경우
import React, { useEffect, useState } from "react";
const App = () => {
const [value, setValue] = useState("");
useEffect(() => {
console.log(`hello useEffect!: ${value}`);
}, [value]);
return (
<div>
<input
type="text"
value={value}
onChange={(e) => {
setValue(e.target.value);
}}
/>
</div>
);
};
export default App;
- value는 state이고 우리가 input을 입력할 때마다 그 값이 변하게 되므로 useEffect도 게속 실행됨!
ⅲ. clean up
- 클린업이란?
- 맨 처음에 화면에 뭔가 나왔을 때도 동작을 하지만 화면에서 없어질 때도 동작을 한다라는 것이 클린 업(clean up)이라고 함
- 클린업 하는 방법
// src/App.js
import React, { useEffect } from "react";
const App = () => {
useEffect(()=>{
// 화면에 컴포넌트가 나타났을(mount) 때 실행하고자 하는 함수를 넣어주세요.
return ()=>{
// 화면에서 컴포넌트가 사라졌을(unmount) 때 실행하고자 하는 함수를 넣어주세요.
}
}, [])
return <div>hello react!</div>
};
export default App;
useEffect는 화면에 컴포넌트가 mount 또는 unmount 됐을 때 실행하고자 하는 함수를 제어하게 해주는 훅
의존성 배열을 통해 함수의 실행 조건을 제어할 수 있음
useEffect에서 함수를 1번만 실행시키고자 할때는 의존성 배열을 빈 배열로 둠
3. useRef
- 저장공간의로서의 useRef, DOM요소 접근 방법으로서의 useRef 활용 방법!!
ⅰ. useRef란?
- DOM 요소에 접근할 수 있도록 하는 React Hook
- HTML 과 JS를 사용했을 때 우리는 특정 DOM을 선택하기 위해서 getElementById, querySelector를 이용함
- 리액트에서도 DOM을 선택해야 할 상황이 생기기 마련 => 이러한 경우 useRef hook을 상용함
- 또한 변수처럼 쓰기 위해서도 useRef를 사용함
- 사용 방법
import { useRef } from "react";
const App = () => {
// ref : reference
const ref = useRef("초기값")
console.log("ref", ref)
return <div></div>
};
export default App;
- ref값 변경하기
import { useRef } from "react";
const App = () => {
// ref : reference
const ref = useRef("초기값")
console.log("ref", ref)
ref.current = "변경값"
console.log("ref2", ref)
return <div></div>
};
export default App;
- 이렇게 설정된 ref값은 컴포넌트가 계속해서 렌더링 되어도 unmount전까지 값을 유지함
- 위에 특징때문에 2가지 용도로 사용됨
- 저장공간
- state와 비슷한 역활을 함 => 다만 state는 변화가 일어나면 렌더링이 일어나고 내부변수들은 초기화됨
- ref에 저장한 값은 렌더링을 일으키지 않음. => 즉, ref의 값 변화가 일어난도 렌더링으로 인해 내부 변수들이 초기화 되는것을 막을 수 있음
- 컴포넌트가 100번 렌더링 => ref에 저장한 값은 유지됨
- 정리
📍 state는 리렌더링이 꼭 필요한 값을 다룰 때 쓰면됨
📍 ref는 리렌더링을 발생시키지 않는 값을 저장할 떼 사용
- DOM
- 렌더링이 되자마자 특정 Input이 focusing 돼야 한다면 useRef를 사용하면 됨
- 저장공간
ⅱ. state와 ref 차이점
import { useRef, useState } from "react";
const style = {
border: "1px solid black",
margin: "10px",
padding: "10px",
};
const App = () => {
const [count, setCount] = useState(0);
const countRef = useRef(0);
const plusStateCountButtonHandler = () => {
setCount(count + 1);
};
const plusRefCountButtonHandler = () => {
countRef.current++;
console.log(countRef.current);
};
return (
<>
<div style={style}>
state 영역입니다. {count} <br />
<button onClick={plusStateCountButtonHandler}>state 증가</button>
</div>
<div style={style}>
ref 영역입니다. {countRef.current} <br />
<button onClick={plusRefCountButtonHandler}>ref 증가</button>
</div>
</>
);
};
export default App;
📌📌 내부 변수는 let 키워드로 선언해서 변수를 사용하면 안되는지
- let 키워드를 사용하게 되면 렌더링시 다시 변수가 초기화 됨 => 함수라서
리렌링이 된다 = 함수가 다시 호출된다 = 내부 변수가 다시 쫙 초기화됨
ⅲ. DOM 접근
- input 태그에는 ref라는 속성이 있는데 이걸 통해 해당 DOM에 요소로 접근할 수 있음
import { useEffect, useRef } from "react";
const App = () => {
const idRef = useRef("");
const pwRef = useRef("");
// 화면이 렌더링 될 때, 어떤 작업을 하고 싶다! - useEffect
useEffect(() => {
// id를 focus
// idRef.current.focus()
// 비밀번호를 focus
pwRef.current.focus();
}, []);
return (
<>
<div>
아이디 : <input type="text" ref={idRef} />
</div>
<div>
비밀번호 : <input type="password" ref={pwRef} />
</div>
</>
);
};
export default App;
ⅳ. 위에 코드에서 아이디가 10자리 입력되면 자동으로 비밀번호 필드로 이동하도록 만들기!
- 내가 만든 코드
import { useEffect, useRef, useState } from "react";
const App = () => {
const idRef = useRef("");
const pwRef = useRef("");
const [id, setId] = useState("");
const handleIdChage = (e) => {
setId(e.target.value);
};
useEffect(() => {
idRef.current.focus();
}, []);
useEffect(() => {
if (id.length >= 10) {
pwRef.current.focus();
}
}, [id]);
return (
<>
<div>
아이디 :{" "}
<input type="text" value={id} onChange={handleIdChage} ref={idRef} />
</div>
<div>
비밀번호 : <input type="password" ref={pwRef} />
</div>
</>
);
};
export default App;
- 강의에서 알려준 내용
import { useEffect, useRef, useState } from "react";
const App = () => {
const idRef = useRef("");
const pwRef = useRef("");
const [id, setId] = useState("");
useEffect(() => {
idRef.current.focus();
}, []);
useEffect(() => {
if (id.length >= 10) {
pwRef.current.focus();
}
}, [id]);
return (
<>
<div>
아이디 :{" "}
<input
type="text"
value={id}
onChange={(e) => setId(e.target.value)}
ref={idRef}
/>
</div>
<div>
비밀번호 : <input type="password" ref={pwRef} />
</div>
</>
);
};
export default App;
'공부 > React' 카테고리의 다른 글
리액트 숙련주차 - 4 (0) | 2023.07.10 |
---|---|
리액트 숙련주차 - 3 (0) | 2023.07.05 |
리액트 숙련주차 - 1 (0) | 2023.06.30 |
리액트 입문주차 1주차 - 5 (0) | 2023.06.29 |
리액트 입문주차 1주차 - 4 (0) | 2023.06.27 |