티스토리 뷰
Hook
state와 생명주기 기능에 갈고리를 걸어 원하는 시점에 정해진 함수를 실행 되도록 만든 것
Hook 규칙
- 최상위에서만 호출한다
- 반복문, 조건문, 중첩 함수 내에서 호출 불가 (호출 순서에 의지하기 때문에 해당 부분을 건너뛰는 일이 생길 수 있다)
- React 함수 내에서만 호출이 가능하다, 일반적인 js에선 사용 불가 컴포넌트 내부에서만 사용할것
Hook의 종류
useState
- 가장 기본적인 hook
- 함수형 컴포넌트 내에서 가변적인 상태를 갖게한다
- 문법 const [state, setState] = useState(초기값);
- 원래 useState의 리턴값은 배열이다, [state, setState] 이렇게 구조분해할당을 통해 값을 받은 것
배치 업데이트
- 컴포넌트가 여러 번 업데이트 되더라도, 실제 DOM 요소에 변경사항을 적용하는 것을 최소화하여 애플리케이션의 성능을 최적화하는 기능
- State() 메서드를 호출하면, 컴포넌트의 상태(State)를 변경하는 것이지만, 실제 변경사항이 적용되는 시점은 render() 함수 호출 후 적용되기때문에 setState() 메서드를 여러번 호출하더라도, 배치로 처리되어 실제 변경사항이 적용되는 시점이 늦춰지게된다
기존 setState 업데이트 방법
- 배치성으로 처리된다, 배치 업데이트. 한꺼번에 변경된 내용을 모아서 한번만 업데이트한다 setNumber에 값을 3번 넣어줬대도 명령을 모아서 한번으로 처리한다
function App() {
const [number, setNumber] = useState(0);
return (
<>
<div>Number State : {number}</div>
<button
onClick={() => {
setNumber(number + 1)
setNumber(number + 1)
setNumber(number + 1)
}}
>
Up
</button>
</>
함수형 setState 업데이트 방법
- setState(함수를 넣고 매개변수로 현재 State를 가져올 수 있다.)
- 명령들을 모아서 순차적으로 한번씩 실행시킨다. 인자 부분에 현재 스테이트를 받고 바뀐 스테이트를 내보내기때문에 최신값을 유지할수있는것 (함수는 인풋과 아웃풋이 있다, 들어오면 내보내야함)
function App() {
const [number, setNumber] = useState(0);
return (
<>
<div>Number State : {number}</div>
<button
onClick={() => {
setNumber((currNum) => currNum + 1);
setNumber((currNum) => currNum + 1);
setNumber((currNum) => currNum + 1);
}}
>
Up
</button>
</>
useEffect
- 렌더링 될 때, 특정한 작업을 수행해야 할 때 설정하는 훅 (ex.alert, console... 등)
- 컴포넌트가 렌더링 또는 리렌더링 시, 컴포넌트가 사라졌을때(컴포넌트가 죽었을 때) 실행된다
- [ ] 의존성 배열(dependency array) 배열에 값을 넣으면, 그 값이 바뀔때만 useEffect을 실행한다
사용 방법
- 컴포넌트가 렌더링 될 때마다 console이 실행된다
useEffect(() => {
console.log("hi useEffect");
}, []);
조건부 실행(의존성 배열)
- 의존성 배열 - useEffect 함수가 끝나는 부분 뒤에 배열 형태로 넣는것
- [ ]안에 값이 바뀔때 useEffect를 실행시키기 위한 조건(값)을 넣는것
- 의존성 배열 넣지 않을 경우, 리렌더링될때마다 useEffect가 실행되었지만, 배열에 값을 넣으면 처음 렌더링 됐을때와 값이 바뀔때만 실행된다(첫 렌더링 시 실행은 무조건임)
이해를 돕기 위한 설명
- 렌더링하면 useEffect가 먼저 실행된다.
- input 에 값을 입력하면 value, 즉 state(상태)가 변경됨
- state가 변경되었기 때문에 App 컴포넌트가 리렌더링된다
- 리렌더링이 되면 useEffect가 다시 실행된다 input 값이 바뀔때마다 리렌더링 되고, useEffect가 계속 실행됨
- useEffect가 실행될때마다 리렌더링이 되고 있는 것
useEffect(() => {
console.log(`hi useEffect : ${value}`);
}, [value]);
return (
<div>
<input
type="text"
value={value}
onChange={(e) => setValue(e.target.value)}
/>
</div>
);
clean Up
- 컴포넌트가 사라졌을때 실행
useEffect(() => {
console.log(`hi useEffect : ${value}`);
return () => {
console.log("나 사라진다아");
};
}, [value]);
useRef
- DOM요소에 접근하고, 변수의 저장공간으로 쓰인다
- state는 변화가 일어나면 다시 렌더링이 일어나 변수들은 초기화 되지만, useRef 저장된 값은 렌더링을 일으키지 않기 때문에 변수들이 초기화되는걸 막을 수 있다
- state - 리렌더링이 꼭 필요한 값에 사용(변화되는 값)
- useRef - 렌더링을 발생시키지 않는 값을 저장할때 사용
사용 방법
- 콘솔을 확인헤 보면 객체형태로 되어있다 ref {current(키) : '초기값'(값)}
- 키에 접근하여 값을 변경하면 변경된 값이 출력된다
- 설정된 값은 컴포넌트가 계속 렌더링 되어도 unmount되기전까지(컴포넌트가 죽기 전까지) 계속 값을 유지한다
const ref = useRef("초기값");
console.log("ref", ref);
ref.current = "변경값";
console.log("ref", ref);
- plusStateCnt는 state로 연결되어있어 버튼을 누르면 화면에서 count가 올라간다
- plusRefCount는 화면 변화는 없지만 콘솔을 확인하면 count가 올라가고 있다
const plusStateCnt = function () {
return setCount(count + 1);
};
const plusRefCount = () => {
countRef.current++;
console.log(countRef.current);
// 버튼을 클릭하면 화면상에 변화는 없지만 콘솔에서는 잘 작동한다
};
return (
<>
<div style={style}>
state 영역입니다 {count} <br />
<button onClick={plusStateCnt}>state 증가</button>
</div>
<div style={style}>
ref 영역입니다 {countRef.current} <br />
<button onClick={plusRefCount}>ref 증가</button>
</div>
</>
Focus 적용 방법(useEffect , useRef)
- 화면이 렌더링 되면, 아이디 포커스
const idRef = useRef(""); // 아이디 포커스
const [id, setId] = useState("");
useEffect(function () {
return idRef.current.focus();
}, [ ]);
아이디 :
<input
type="text"
ref={idRef}
value={id}>
ID가 10자리를 넘으면 비밀번호에 focus 옮기는 방법
- id의 값이 바뀔때마다 useEffect가 실행돼야하니까 의존성 배열에 변경 기준 값으로 id를 넣어줌
useEffect(() => {
if (id.length >= 10) {
pwRef.current.focus();
}
}, [id]);
<div>
아이디 :
<input
type="text"
ref={idRef}
value={id}
onChange={(e) => {
setId(e.target.value);
// State로 작성할 경우, useState는 배치업데이트로 setId 값이 변해도 바로 적용되는게 아니기때문에
// 11자리를 입력해야 포커스가 넘어간다, useEffect를 사용하는게 더 효과적임
// id(id.length >=10){
// pwRef.current.focus();
// }
}}
/>
</div>
<div>
비밀번호 <input type="password" ref={pwRef} />
</div>
useContext
- 전역 데이터 관리, 어떤 컴포넌트든 전역 데이터에 접근할 수 있다
- useContext 사용 시 Provider에서 제공한 value가 달라진다면(GF) useContext를 사용하고 있는 모든 컴포넌트가 리렌더링되므로 value 부분 신경쓸것
사용 방법
- 목적 : GF가 Child한테 정보를 전달하여 Child한테 내용을 출력하도록 하자
- 하위 컴포넌트가 모두 접근 할 수 있는 전역객체 FamilyContext생성
- 정보를 보낼 부모와 컨텍스트 연결, 정보를 내려줄 자식 컴포넌트 연결
- 기존에는 props를 이용했지만 Provider value={{ houseName, pocketMoney }} 으로 데이터 전달
- 데이터를 받은 Child 컴포넌트는 GF가 FamilyContext에 담아 보낸 정보를 useContext를 통해 변수에 담아 꺼낸다(컨텍스트 이름을 넣어야함)
export const FamilyContext = createContext(null);
// 하위컴포넌트들이 사용할 수 있는 컴포넌트
* 최상위 컴포넌트
function App() {
return <GrandFather />;
}
const houseName = "스파르타"; // 집안 이름
const pocketMoney = 10000; // 손자 용돈
return (
// Father 컴포넌트 아래로 FamilyContext를 제공해준다는 의미
// props를 value로 보낸다(객체니까 {} 안에 담아야함)
<FamilyContext.Provider value={{ houseName, pocketMoney }}>
<Father />
</FamilyContext.Provider>
);
}
function Child() {
// GF가 FamilyContext에 담아 보낸 정보를 useContext를 통해 변수에 담아 꺼낸다(컨텍스트 이름을 넣어야함)
const data = useContext(FamilyContext);
console.log("data", data);
return (
<div>
나는 이 집안의 막내에요 <br />
할아버지가 우리 집 이름은 <span style={style}>{data.houseName}</span>라고
하셨어요 <br />
게다가 용돈도 <span style={style}>{data.pocketMoney}</span>원이나 주셨어요
</div>
);
}
'TIL > React' 카테고리의 다른 글
[리액트 React] 실제 돔과 가상 돔 (DOM, Virtual DOM) (0) | 2024.01.30 |
---|---|
[리액트 React] 최적화 (메모이제이션) (0) | 2024.01.26 |
[리액트 React] 스타일 컴포넌트(Styled Components) 지역,전역 스타일 (0) | 2024.01.24 |
[리액트 React] 투두리스트 TodoList, Jsx문법, 컴포넌트 (0) | 2024.01.23 |
[리액트 React] 투두리스트 만들기(TodoList) (0) | 2024.01.22 |