본문 바로가기
개발(라이브러리,프레임워크)/react.js & react native

Hooks

by zieunee 2022. 1. 9.
반응형

동기

  1. state로 관리하면 재사용하기가 어려웠음. 상태를 this.state를 다른데서 다르게 관리 해야하니까..
  2. 계층 변화 없이 상태 관련 로직을 재사용할 수 있도록 도와준다.
  3. 복잡한 컴포넌트 경우 상태관련 로직을 한 공간안에서 묶여있기 때문에 작게 분리하는 것은 불가능 하며, 테스트 하기도 어려움. 또한 상태라이브러리는 많은 추상화, 파일들의 건너뛰기를 요구 하며 재사용하기 어려워졌다.

정리: class의 단점을 보완하면서 / 라이프사이클 등과 관련된 함수를 재사용 가능하도록 한다.

규칙

  1. 최상위에서만 hook을 호출해야 한다. 반복문, 조건문, 중첩된 함수내에서 hook실행 x
  2. React함수 컴포넌트 내에서만 hook을 호출해야 한다.

State Hook

일반

import React from 'react';

export default class Test extends React.Component {
    state = { count:0 } ;

    render() {
        const { count } = this.state;
        return (
            <div>
                <p>{count}</p>
                <button onClick={this.click}>click me</button>
            </div>
        )
    }
    click = () => {
        this.setState({count: this.state.count+1 });
    }
}

useState 사용

import React from 'react';
//useState >> count
export default function Test2() {
    const [count, setCount] = React.useState(0); //() 이 안의 값은 count의 초기값
    // 첫번째 인자: 업데이트가 되었을때 변경될 값
    // 두번째 인자: count를 바꾸는 함수 >> click 함수를 다시 실행시킨다.
    return (
        <div>
            <p>{count}</p>
            <button onClick={click}>click me</button>
        </div>
    );
    function click() {
        setCount(count + 1); 
    }
}

useState 객체로 사용

import React from 'react';
//useState >> { count : 0 } 객체로 사용하기
export default function Test3() {
    const [state, setState] = React.useState({count : 0}); //() 이 안의 값은 count의 초기값
    // 첫번째 인자: 업데이트가 되었을때 변경될 값
    // 두번째 인자: count를 바꾸는 함수 >> click 함수를 다시 실행시킨다.
    return (
        <div>
            <p>{state.count}</p>
            <button onClick={click}>click me</button>
        </div>
    );
    function click() {
        setState((state) => {
            return {
                count: state.count + 1
            }
        }); 
    }
}

setState((state) ....

*중요!! 어떤것을 의존해서 사용하는지? dependency 외부의 state에 의존적이지않고 함수에 넣어두면 인자로 들어오니 의존적이지 않다.

useState를 사용해서 상태 처리하는 이유?

컴포넌트 사이에서 상태 관련된 로직을 재사용하기 어려움

복잡한 컴포넌트 이해하기 어려움

class 는 컴파일 단계에서 코드를 최적화 하기 어렵게 만든다.

this.state 는 render 사이에 레퍼런스를 공유하기 떄문에 문제가 발생할 수 있다. (class컴포넌트)

function컴포넌트는 공유안함

  • useState
  • useState는 state를 대체할 수 있음

Effect Hook

useEffect

useEffect로 전달된 함수는 지연 이벤트 동안 레이아웃 배치와 그리기를 완료 한 후 발생한다.

vs useLayoutEffect : 수행되는 타이밍이 차이가 난다. → 모든 dom 변경 이후에 동기적으로 발생한다.

useEffet는 라이프 사이클 훅을 대체할 수 있음

  • componentDidMount
  • componentDidUpdate
  • componentWillMount

class 컴포넌트 : state 가 변경되면 didmount, didupdate둘다 변경해줘야 한다 > 중복 코드가 생김

기능

  • 데이터 가져오기
  • 구독 설정하기
  • 수동으로 리액트 컴포넌트의 DOM을 수정하기
React.useEffect(() => {
	console.log("componentDidMount");
	return () => {
		//clean up
		//componentWillUnMount 
	};
}, []); // , [] 는 최초에만 실행한다. 라는 의미

React.useEffect(() => {
	console.log("componentDidMount & componentDidUpdate by count");
	return () => {
		//clean up by count
	};
}, [count]); //count가 변했을 때만 실행 
  • , [] 는 최초에만 실행한다. 라는 의미
  • return 하면 clean up 기능이 있음
  • 두번째 인자 > 배열.. 실행하는 타이밍 조절
    • 항상 렌더 될떄 실행>> 최초에만 실행 등등으로 바꿀 수 있음

Custom Hook

useSomething

화면크기 바뀔때마다 width 바뀌게 만들기

import React, {useState, useEffect} from 'react';

export default function useWindowWidth(){
    const [width, setWidth] = useState(window.innerWidth);
    useEffect(() => {
        const resize = () => {
            setWidth(window.innerWidth);
        };
        window.addEventListener("resize" , resize);
        return () => {
            window.removeEventListener("resize" , resize);
        };
    }, []);
    return width;
}

useHasMounted vs withHasMounted

App.js

import logo from './logo.svg';
import './App.css';
import Test3 from './components/Test3';
import useWindowWidth from './hooks/useWindowWidth';
import withHasMounted from './hocs/withHasMounted';

function App({ hasMounted }) {
  console.log(hasMounted);
  const width = useWindowWidth();
  const hasMountedFromHooks = useHasMounted();

  console.log(hasMounted, hasMountedFromHooks);
  return (
    <div className="App">
      <header className="App-header">
        <img src={logo} className="App-logo" alt="logo" />
        <Test3/>
        {width}
      </header>
    </div>
  );
}

export default withHasMounted(App);

withHasMounted.jsx

import React, {useState, useEffect} from 'react';

export default function withHasMounted(Component){
    class NewComponent extends React.Component {
        state = {
            hasMounted: false,
        };
        render(){
            const { hasMounted } = this.state
            return <Component {...this.props} hasMounted = {hasMounted} />
        }
        componentDidMount() {
            this.setState({hasMounted : true});
        }
    }
    NewComponent.displayName = `withHasMounted(${Component.name})`;
    return NewComponent;
}

useHasMounted.js

export default function useHasMounted(){
    const [hasMounted] = useState(false);

    useEffect(() => {
      setHasMounted(true);  
    },[]);

    return hasMounted;
}

Additional Hooks

useReducer

  • 다수의 하윗값을 포함하는 복잡한 정적 로직을 만드는 경우
  • 다음 state가 이전 state에 의존적일 경우
  • Redux
import { useReducer } from 'react';

const reducer = (state, action) => {
    if(action.type === 'PLUS') {
        return {
            count: state.count +1,
        };
    }
    return newState
}
export default function Test2() {
    const [state, dispatch] = React.useReducer(reducer, {count: 0});
    return (
              <div>
            <p>{state.count}</p>
            <button onClick={click}>click me</button>
        </div>
    );
        function click(){
                dispatch({type: "PLUS"}) // state의 count를 1 올려준다.
        }
}
  • reducer: state를 변경하는 로직이 담겨있는 함수
  • dispatch: dispatch를 통해서 action객체를 내려준다.
  • action : 객체이고 필수 프로퍼티로 type을 가진다.
    • PLUS라는 action을 dispatch로 넘겨주어서 action의 type이 해당 값이면 state를 변경한다.

useMemo

import { useState , useMemo} from 'react';

function sum(persons){
    return person.map((person) => person.age).reduce((l,r) => l+r , 0)
}
export default function Test2() {
    const [value, setValue] = useState('');
        const [persons] = usetState([
            { name: "Mark", age : 39 },
            { name: "Hanna", age : 29 }
        ]);

        const count = useMemo(() => {
                return sum(persons);
        }, [persons]); 
//dependency list를 인자로 받는다. 
//persons가 변했을 때만 실행하겠다.
//person의 의존적이다.

        const click = useCallback(()=> {
            //최초의 값만 넣고싶어 .. 계속 갱신 xx 하고 싶다 라면? >>
            console.log(value);
        }, []); // 이렇게 생성하기 , 이전 함수내용 가지고 있음 
    return (
              <div>
                        <input value = {value} onChange={change} />
            <p>{count}</p>
                        <button onClick={click}>click</button>
        </div>
    );
        function change(e){
                setValue(e.target.value);
        }
}

다른 동작을 할때마다 render가 새로 된다. > sum함수가 불필요하게 실행된다. person에 의존적으로 계산이 된다.

person이 같으면 굳이 리렌더링 될 필요 없음 .. 이걸 해결하기 위해 useMemo를 사용한다.

useCallback

최초의 값만 넣고싶어 .. 계속 갱신 xx 하고 싶다 라면? }, []); 이 내용 추가해주면 이전 함수내용 가지고있을 수 있다.

useCallback은 안에 들어있는 함수를 언제 세팅해줄것인지? [] 이 dependency list에 의존적으로 결정할 수 있음

언제 사용함??

컴포넌트 최적화할때 사용한다.

useRef

import { useState , useRef ,createRef } from 'react';

export default function Test2() {
  const [value, setValue] = useState('');
    const inpt1Ref = createRef();
    const inpt2Ref = useRef();
    console.log(input1Ref.current, input2Ref.current);
     return (
              <div>
                    <input value={value} onChange={change} />
                    <input ref={inpt1Ref}/>
                    <input ref={inpt2Ref}/>
        </div>
    );
        function change(e){
                setValue(e.target.value);
        }
} 

React Router Hooks

useHistory

props가 들어오는 history랑 같은것을 구해서 사용

import { useHistory } from 'react';

export default function Test2() {
    const history = useHistory();
    function login(){
        setTimeout(()=> {
            history.push("/");
        });
    }
    return <button onClick={login}>login</button>

} 

useParams

반응형

'개발(라이브러리,프레임워크) > react.js & react native' 카테고리의 다른 글

Redux 를 React에 연결(라이브러리x)  (0) 2022.01.21
Redux 개요, 사용  (0) 2022.01.20
라우팅  (0) 2021.09.26
컴포넌트  (0) 2021.09.24
라이프사이클  (0) 2021.09.23