[React] Hooks

DoDodo·2023년 3월 6일
0

React

목록 보기
3/5
post-thumbnail

Hooks는 함수형 컴포넌트에서도 상태 관리를 할 수 있는 기능을 제공한다.

1. useState
가장 기본적인 Hooks.
const [ value, setValue] = useState(0);
파라미터에는 상태의 기본값을 넣어준다.

import React from 'react';

const Info = () => {
    const [name, setName] = useState('');
    const [nickname, setNickname] = useState('');

    const onChangeName = e => {
        setName(e.target.value);
    };

    const onChangeNickname = e => {
        setNickname(e.target.value);
    };

    return (
        <div>
            <div>
                <input value={name} onChange = {onChangeName} />
                <input value={nickname} onChange = {onChangeNickname} />
            </div>
            <div>
                <div><b>이름:</b>{name}</div>
                <div><b>닉네임:</b>{nickname}</div>
            </div>
           
        </div>
    );
};

export default Info;

2. useEffect
리액트의 컴포넌트가 렌더링될 때마다 특정 작업을 수행하도록 설정할 수 있는 Hooks.

  1. 화면에 맨 처음 렌더링될 때만 실행하고, 업데이트될 때는 실행하지 않고 싶을 때
useEffect(() => {
        console.log({
            name, nickname
        });
    }, []); // useEffect의 두번째 파라미터로 []를 넣어준다
2. 특정 값이 업데이트 될 때만 실행하고 싶을 때
```
useEffect(() => {
    console.log({
        name, nickname
    });
}, [name]); // useEffect의 두번째 파라미터로 검사하고싶은 값을 넣어준다
```
3. 컴포넌트의 언마운트 전이나 업데이트 직전에 어떤 작업을 수행하고 싶을 때
```
useEffect(() => {
    console.log({
        name, nickname
    });
    return () => { // cleanup 함수를 반환한다
        console.log('cleanup'); 
        console.log(name); // 업데이트 직전의 값을 반환한다
    }
}, [name]);
```

3. useReducer
reducer는 변화를 일으키는 함수이다. 현재 상태, 그리고 업데이트를 위해 필요한 정보를 담은 action값을 전달받아 새로운 상태를 반환하는 함수이다. (추후 Redux에서 자세히)
useState보다 더 다양한 컴포넌트 상황에 따라 다양한 상태를 다른 값으로 업데이트 하고 싶을 때 사용하는 Hooks
가장 큰 장점은 컴포넌트 업데이트 로직을 컴포넌트 바깥으로 꺼낼 수 있다는 것.

import React, {useReducer} from 'react';

function reducer(state, action) {
    //action.type에 따라 다른 작업 수행
    switch (action.type){
        case 'INCREMENT':
            return {value: state.value + 1};
        case 'DECREMENT':
            return {value: state.value - 1};
        default:
            //아무것도 해당되지 않을 때 기존 상태 반환
            return state;
    }
}

const Counter = () => {
    const [state, dispatch] = useReducer(reducer, {value:0}); // 리듀서 함수, 리듀서 함수의 기본 값을 넣어줌

    return (
        <div>
            <p>현재 카운터 값은 <b>{state.value}</b>입니다.</p>
            <button onClick={() => dispatch({type:'INCREMENT'})}>+1</button>
            <button onClick={() => dispatch({type:'DECREMENT'})}>-1</button>
        </div>
    );
};

export default Counter;
import React, { useReducer } from 'react';

function reducer(state, action) {
    return {
        ...state, 
        [action.name]: action.value
    };
}


const Info = () => {
    const [ state, dispatch] = useReducer(reducer, {
        name: '',
        nickname: ''
    });

    const {name, nickname} = state;
    const onChange = e => {
    	//e.target을 action값으로 사용 : 인풋 개수가 많아져도 코드를 깔끔하게 유지할 수 있는 장점이 있다!
        dispatch(e.target); 
    };

    return (
        <div>
            <div>
            	<input value={name} onChange = {onChange} />
                <input value={nickname} onChange = {onChange} />
            </div>
            <div>
                <div><b>이름:</b>{name}</div>
                <div><b>닉네임:</b>{nickname}</div>
            </div>
           
        </div>
    );
};

export default Info;

state : 현재 가리키고 있는 상태
dispatch : 액션을 발생시키는 함수. dispatch(action) 형태로 리듀서 함수가 호출되는 구조

4. useMemo
함수형 컴포넌트 내부에서 발생하는 연산을 최적화 할 수 있다. 렌더링 과정에서 특정 값이 바뀌었을 때만 연산을 실행하고, 원하는 값이 바뀌지 않았다면 이전에 연산했던 결과를 다시 사용한다.

const avg = useMemo(() => getAverage(list), [list]);

5. useCallback
렌더링 성능을 최적화해야 하는 상황에서 사용. 만들어 놨던 함수를 재사용할 수 있다. useCallback의 첫번째 파라미터에는 생성하고 싶은 함수를 넣고, 두 번째 파라미터에는 배열을 넣는다. 이 배열은 어떤 값이 바뀌었을 때 함수를 새로 생성해야 하는 지 명시한다.

import React from 'react';

const getAverage = numbers => {
    if (numbers.length === 0) return 0;
    const sum = numbers.reduce((a,b)=> a+ b);
    return sum/numbers.length;
}


const Average = () => {
    const [list, setList] = useState([]);
    const [number, setNumber] = useState('');

    const onChange = useCallback ( e => {
        setNumber(e.target.value);
    }, []); // 컴포넌트가 처음 렌더링 될 때만 함수 실행
    
    const onInsert = useCallback( e => {
        const nextList = list.concat(parseInt(number));
        setList(nextList);
        setNumber('');
    }, [number, list]); //number,list의 변경이 생기면 함수 실행

    const avg = useMemo(() => getAverage(list), [list]);

    return (
        <div>
            <input value = {number} onChange = {onChange}/>
            <button onClick={onInsert}>등록</button>
            <ul>
                {list.map((value, index) => (
                    <li key={index}>{value}</li>
                ))}
            </ul>
            <div><b>평균값:</b>{avg}</div>
            
        </div>
    );
};

export default Average;

함수 내부에서 상태 값에 의존해야 할 때는 그 값을 반드시 두 번째 파라미터 안에 포함시켜 주어야 한다. onChange의 경우 기존의 값을 조회하지 않고 바로 설정만 하기 때문에 배열이 비어있어도 상관 없지만, onInsert의 경우 기존의 numbers와 list를 조회해서 nextlist를 생성하기 때문에 배열 안에 number와 list를 넣어 주어야 한다.

6. useRef
함수형 컴포넌트에서 ref를 쉽게 사용할 수 있도록 해줌.

import React from 'react';

const getAverage = numbers => {
    if (numbers.length === 0) return 0;
    const sum = numbers.reduce((a,b)=> a+ b);
    return sum/numbers.length;
}

const Average = () => {
    const [list, setList] = useState([]);
    const [number, setNumber] = useState('');
    const inputEl = useRef(null);

    const onChange = useCallback ( e => {
        setNumber(e.target.value);
    }, []); // 컴포넌트가 처음 렌더링 될 때만 함수 실행
    
    const onInsert = useCallback( e => {
        const nextList = list.concat(parseInt(number));
        setList(nextList);
        setNumber('');
        inputEl.current.focus() //useRef로 만든 객체 안의 current 값이 실제 엘리먼트를 가리킴
    }, [number, list]); //number,list의 변경이 생기면 함수 실행

    const avg = useMemo(() => getAverage(list), [list]);

    return (
        <div>
            {/*등록 버튼을 눌렀을 때 포커스가 인풋 쪽으로 넘어간다 */}
            <input value = {number} onChange = {onChange} ref = {inputEl}/>
            <button onClick={onInsert}>등록</button>  
            <ul>
                {list.map((value, index) => (
                    <li key={index}>{value}</li>
                ))}
            </ul>
            <div><b>평균값:</b>{avg}</div>
            
        </div>
    );
};

export default Average;

7. 커스텀 hooks
useReducer -> useInputs라는 Hook으로 분리한다.

import {useReducer} from 'react';

function reducer(state, action) {
	return {
    	...state,
        [action.name]: action.value
    };
}

export default function useInputs(initialForm) {
	const [ state, dispatch] = useReducer(reducer, initialForm);
    const onChange = e => {
    	dispatch(e.target);
};
return [state, onChange];
}
import React from 'react';
//다른 컴포넌트에서 hooks 호출
import useInputs from './useInputs';

const Info = () => {
	const [state, onChange] = useInputs({
    	name: '',
        nickname: ''
    });
    const {name, nickname} = state;
    
    return (
    ...
    );
 };
 
 export default Info;
  • 클래스형 컴포넌트를 작성하지 않고도 함수형 컴포넌트 + hooks를 사용하여 대부분의 기능을 구현할 수 있다. 이렇게 쓰는 걸 더 권장합!

0개의 댓글