본문 바로가기
Programming/React.js

7. Hooks

by _S0_H2_ 2022. 3. 6.
728x90
반응형

useState

기본 sate 사용 예제

- App.js

import Counter from './Counter';

const App = () => {
  return <Counter />;
};

export default App;

- Counter.js

import {useState} from 'react';

const Counter = () =>{
    const [value, setValue] = useState(0);

    return(
        <div>
            <p> 현재 카운터 값은 <b> {value}</b> 입니다. </p>
            <button onClick={() => setValue(value + 1)}> +1 </button>
            <button onClick={() => setValue(value - 1)}> -1 </button>
        </div>
    )
}

export default Counter;

 

 

여러개의 state 사용하기

-App.js

import Info from './Info';

const App = () => {
  return <Info />;
};

export default App;

-Info.js

import {useState} 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>
                <b>이름 : </b> {name}
                <b>닉네임 : </b> {nickname}
            </div>
        </div>
    )
}

export default Info;

 

input에 입력하는 값이 렌더링 되어 나온다.

 

useEffect

useEffect는 클래스형 컴포넌트의 componentDidMount와 componentDidUpdate 를 합친 형태로 볼 수 있다. 다음을 작성하고 확인해보자.

-Info.js

import {useEffect, useState} from 'react';

const Info = () =>{
    const [name, setName] = useState('');
    const [nickname, setNickname] = useState('');
    
    useEffect(() => {
        console.log('렌더링이 완료되었습니다.');
        console.log({ name, nickname});
    });

	... // 위와 동일
}

export default Info;

업데이트 될 때는 useEffect의 함수가 실행되지 않도록하려면(처음 렌더링될 때만 실행됨) 함수의 파라미터로 빈 배열을 넣어주면된다.

useEffect(() => {
        console.log('마운트 될 때만 실행됩니다.');
    }, []);

만약 특정 값이 변경될 때만 호출하고 싶다면 다음과 같이 작성해보자.

useEffect(() => {
        console.log(name);
    }, [name]);

만약 컴포넌트가 언마운트되기 전이나 업데이트되기 직전에 어떠한 작업을 수행하고 싶다면 cleanup함수를 반환해주어야한다.

useEffect(() => {
        console.log('effect');
        console.log(name);
        return () => {
            console.log('cleanup');
            console.log(name);
        };
    }, [name]);

- App.js

import Info from './Info';
import {useState} from 'react';

const App = () => {
  const [visible, setVisible] = useState(false);
  return (
  <div>
    <button onClick = {() => { setVisible(!visible)}}> 
      {visible ? '숨기기' : '보이기'}
    </button>
    {visible && <Info />}
  </div>
  )
};

export default App;

버튼을 클릭하면서 렌더링 과정을 볼 수 있다.

처음 렌더링 시 effect
ㄱ 입력 시 cleanup과 effect
숨기기 클릭시 cleanup

 

useReducer

ueReducer는 useState보다 더 다양한 컴포넌트 상황에 따라 다양한 상태를 다른 값으로 업데이트 하고 싶을 때 사용하는 hook이다. 현재 상태, 업데이트를 위해 필요한 정보를 담은 action 값을 전달받아 새로운 상태를 반환하는 함수이다. 

리듀서 함수에서 새로운 상태를 만들 때는 반드시 불변성을 지켜주어야 한다.

- Counter.js

import {useReducer} from 'react';

function reducer(state, action){
    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;

- App.js

import Counter from './Counter';

const App = () => {
  return <Counter />
};

export default App;

Info 예제에서 useState는 각각 관리하였는데, useReducer를 사용하면 input 태그에 name을 할당하고 e.target.name을 참조하여 작업을 처리할 수 있다. 훨씬 코드가 간결해지는 것을 볼 수 있다.

- Info.js

import {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 => {
        dispatch(e.target);
    }

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

export default Info;

- App.js

import Info from './Info';

const App = () => {
  return <Info />
};

export default App;

useMemo

useMemo는 함수 컴포넌트 내부에서 발생하는 연산을 최적화할 수 있다. 평균값을 계산하는 코드를 작성해보자.

useMemo는 li의 내용이 변경될 때만 호출된다.

 - Average.js

import {useState, useMemo} from 'react';

const getAverage = numbers =>{
    console.log('평균값 계산중');
    if (numbers.length === 0) return 0;
    const sum = numbers.reduce((a, b) => a + b);
    return sum / numbers.length;
}

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

    const onChange = e =>{
        setNumber(e.target.value);
    }

    const onInsert = () => {
        const nextList = li.concat(parseInt(number));
        setList(nextList);
        setNumber('');
    }

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

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


export default Average;

- App.js

import Average from './Average';

const App = () => {
  return <Average />
};

export default App;

useCallback

useCallback은 렌더링 성능을 최적화해야하는 상황에서 사용한다. 컴포넌트의 렌더링이 자주 발생하거나 렌더링해야 할 컴포넌트의 개수가 많아지면 이 부분을 최적화해주는 작업이 필요하다.

import {useState, useMemo, useCallback} from 'react';

const getAverage = numbers =>{
    console.log('평균값 계산중');
    if (numbers.length === 0) return 0;
    const sum = numbers.reduce((a, b) => a + b);
    return sum / numbers.length;
}

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

    const onChange = useCallback(e =>{
        setNumber(e.target.value);
    }, []);

    const onInsert = useCallback(() => {
        const nextList = li.concat(parseInt(number));
        setList(nextList);
        setNumber('');
    }, [number, li])

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

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


export default Average;

 

useRef

useRef를 사용하여 ref를 설정하면 useRef를 통해 만든 객체 안의 current 값이 실제 element를 가리킨다. 등록 후에 포인터가 input box를 가리킨다.

- Average.js

import {useState, useMemo, useCallback, useRef} from 'react';

const getAverage = numbers =>{
    console.log('평균값 계산중');
    if (numbers.length === 0) return 0;
    const sum = numbers.reduce((a, b) => a + b);
    return sum / numbers.length;
}

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

    const onChange = useCallback(e =>{
        setNumber(e.target.value);
    }, []);

    const onInsert = useCallback(() => {
        const nextList = li.concat(parseInt(number));
        setList(nextList);
        setNumber('');
        inputEl.current.focus();
    }, [number, li])

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

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


export default Average;

이외 customized hook을 만들어 서로 다른 component에서 사용할 수 있다. 또한 다른 개발자가 만든 hooks도 라이브러리에서 설치하여 사용할 수 있다고 한다.

728x90
반응형

'Programming > React.js' 카테고리의 다른 글

9. 컴포넌트 성능 최적화  (0) 2022.08.24
8. Component styling  (0) 2022.03.08
6. component의 lifecycle method  (0) 2022.03.06
5. 반복적으로 Component 사용하기  (0) 2022.03.05
4. ref:DOM에 이름 달기  (0) 2022.03.05