React Hooks Api

IvanSelah·2022년 3월 18일
0
post-thumbnail

bind는 함수를 사전 구성할 수 있게 해준다.
createTask.bind(null, taskText); // taskText는 호출예정인 함수의 첫번째 인자가 됨.

✅ 두 개의 상태 업데이트 함수 호출
  const navigateHandler = (navPath) => {
    setCurrentNavPath(navPath);
    setDrawerIsOpen(false);
  }

하나의 함수에 하나 이상의 상태 변경함수가 있다면 개수에 따른 스케쥴링이 아닌
단 하나의 스케쥴링으로 관리한다.(하나의 동기화 프로세스와 같이 실행)
📌 컴포넌트의 재실행, 재평가로 인한 2번의 변경 작업이 아니고
함수 내의 모든 상태 변경을 하나의 작업으로 결합해 놓은 스케줄

useImperativeHandle

const submitHandler = (event) => {
    event.preventDefault();
    if (formIsValid) {
      props.onLogin(emailState.value, passwordState.value);
    } else if (!emailIsValid) {
      emailInputRef.current.focus(); <== ✅
    } else { 
      passwordInputRef.current.focus(); <== ✅
    }
};

<form onSubmit={submitHandler}>
  <CustomInput
    ref={emailInputRef}
    id='email' 
    ...
  />
  <CustomInput
    ref={passwordInputRef}
    id='password'
    ...
  />
  <div className={classes.actions}>
    <Button submit={onLogin} type='submit' className={classes.btn}>
      Login
    </Button>
  </div>
</form>

const CustomInput = React.forwardRef((props, ref) => {
  const inputRef = useRef();

  const activate = () => {
    inputRef.current.focus();
  };
  
  // 👍 컴포넌트에서 넘겨준 ref를 독립적으로 컨트롤 할 수 있게 해준다.
  useImperativeHandle(ref, () => {
    return {
      focus: activate, <== ✅ 외부에서 접근할 수 있는 무엇가를 설정
    };
  });

  return (
    <div className={`${classes.control} ${props.isValid === false ? classes.invalid : ''}`}>
      <label htmlFor={props.id}>{props.label}</label>
      <input ref={inputRef} type={props.type} id={props.id} value={props.value} onChange={props.onChange} onBlur={props.onBlur} />
    </div>
  );
});

Context API

const AuthContext = React.createContext({
  isLogginIn: false,
  onLogout: () => {},
  onLogin: (email, password) => {},
});

export const AutoContextProvider = ({ children }) => {
  const [isLoggedIn, setIsLoggedIn] = useState(false);

  const loginHandler = (email, password) => {
    // We should of course check email and password
    // But it's just a dummy/ demo anyways
    setIsLoggedIn(true);
  };

  const logoutHandler = () => {
    setIsLoggedIn(false);
  };

  return (
    <AuthContext.Provider
      value={{
        isLogginIn: isLoggedIn,
        onLogout: logoutHandler,
        onLogin: loginHandler,
      }}
    >
      {children}
    </AuthContext.Provider>
  );
};

export default AuthContext;

<DiaryStateContext.Provider value={{ dataList, deleteData, modifyData, bringData, modifyModalVisible, targetData }}>❌ 이렇게 모두 전달하게 되면 안됨

💡 Provider 도 Component 이므로 props 변경(state)되면 재생성된다. 그 아래 모든 컴포넌트가 강제로 재생성 된다.즉, dataList state가 변경될때마다 리렌더링 발생한다. 그러면 최적화 했던 것들이 전부 풀리게 된다.
⭐️ 해결책 => Context를 중첩해서 만들어야 한다. useMemo로 재생성안되게 묶어서 보냄

  const useStateProps = useMemo(() => {
    return { deleteData, modifyData, bringData, modifyModalVisible, targetData };
  }, [deleteData, modifyData, bringData, modifyModalVisible, targetData]);            

useEffect

React는 브라우저가 모두 그려질 때까지 useEffect의 수행을 지연하기때문에 다른 작업에 영향을 주지 않는다. effect 함수 안에서 참조되는 모든 값은 의존성 값의 배열에 드러나야 한다.
부모컴포넌트에 useEffect는 자식컴포넌트들의 useEffect가 다 실행될때가지 실행되지 않는다.

useState

여러번 호출하면 batch(함께 묶다)하여 state를 업데이트 한다. 매번 호출 순서대로 바로 업데이트하지 않고 인자로 전달된 객체들을 하나로 합치는 작업을 한다(오브젝트 컴포지션)
함수형업데이트 => 객체형태가 아니라 함수로 전달하면 오브젝트 컴포지션을 하지 않고 호출된 순서대로 큐에 넣고 큐에 넣어진 대로 함수를 실행한다. 참고 : https://darrengwon.tistory.com/788

  const onClickPlus = () => {
     setCount(count + 1);
     setCount(count + 1);
     setCount(count + 1);
   };

함수형 업데이트
  const onClickPlus = () => {
    setCount((curr) => curr + 1);
    setCount((curr) => curr + 1);
    setCount((curr) => curr + 1);
  };

useRef

DOM 조작 및 컴포넌트 내에서 변수 관리에 사용 (자동 렌더 안됨), useRef 로 관리하고 있는 변수는 비동기 처리가 아니므로 바로 조회 가능

useMemo

다른 state에 변화로 인해 불필요하게 계속 렌더가 될 때, Memo는 'memoized' 이전에 계산 한 값을 재사용한다는 의미로 useMemo 안에 callback 이 리턴하는 값을 리턴한다.(값을 리턴한다 ⭕️, 함수 리턴 ❌)

  const listItems = useMemo(() => [5, 3, 1, 10, 9], [])

📚 고차컴포넌트 : 컴포넌트안에 컴포넌트를 넣고 강화된(또다른) 컴포넌트를 반환해준다.

React.memo === memo

React는 컴포넌트를 렌더링하고 결과를 메모이징(Memoizing) 한다. 그리고 다음 렌더링이 일어날 때 props가 같다면(전달된 props가 변하지 않는다면), React는 메모이징된 내용을 재사용한다. (가상DOM확인하지 않고 => shallow 비교 후 컴포넌트 재사용한다)

function MovieViewsRealTime() {
	const [title, setTitle] = useState();
    const [relaseDate, setRelaseDate] = useState();
    const [views, setViews] = useState();
    
    useEffect(()=>{
    	...data fetch views 업데이트 
    }, [...])
    
    return (
    <div>
      <Movie title={title} releaseDate={releaseDate} />
      Movie views: {views}
    </div>
  );
}

주기적으로 서버에서 데이터를 가져와서 views를 업데이트 한다고 한다면 veiws가 업데이트 될때마다 MovieViewsRealTime 컴포넌트 또한 리렌더링 되고 이때 Movie 컴포넌트는 title과 releaseDate가 같음에도 불구하고 리렌더링이 발생한다. 이럴 때 사용할 수 있다.

function Movie(){ <= 여기 또는 
 ....
}
export default React.memo(Movie) <= 여기

📌 두번째 인자로 callback 을 전달하여 원하는 값만을 shallow 비교 할 수 있다.
memo 로 감쌌지만 리렌더링이 발생할 때, 객체 안에 count의 값은 같을 수 있으나 obj의 주소값이 변경되었으므로 다르다고 판단하여 리렌더링이 발생한다. 이럴 때 memo 두번째 인자로 판변하는 callback 함수를 전달한다. (true 같음, false 변경됨)
특정 props를 비교해서 렌더링을 컨트롤하는 것은 이후 버그를 발생시킬 수 있으므로 신중하게 한다. 어떠한? 계산을 해야 하는 경우 최신을 반영하기 위해 함수형업데이트를 사용해야 한다.

const areEqual = (preProps: { obj: { count: number } }, nextProps: { obj: { count: number } }) => {
  if (preProps.obj.count === nextProps.obj.count) {
    return true;
  }
  return false;
};

const ConterB = memo(({ obj }: { obj: { count: number } }) => {
  useEffect(() => {
    console.log('ConterB Update');
  });
  return <div>{obj.count}</div>;
});

const MemoCounterB = memo(ConterB, areEqual);

useCallback

함수를 메모이징하여 함수가 다시 생성되지 않도록 한다.
특정 디펜던시에서만 업데이트 되도록 설정, 함수형 업데이트를 활용해서 함수가 다시 생성되지 않아도 최신 값을 업데이트 하도록 설정가능

useReducer

state를 업데이트 함에 있어서 다른 state에 의존한다면 하나의 state로 병합하는 것이 좋다.
{} 객체로 묶은 state나 더 복잡하다면 useReducer 를 사용한다.
useReducer 를 사용하면 관련있는 state 끼리 관리할 수 있다.
useReducer 를 사용하면 최신 state를 스냅샷 한다.

type ActionType = {
  type: 'inc' | 'dec';
};

function reducerFunc(count: number, action: ActionType) {
  switch (action.type) {
    case 'inc':
      return count + 1;
    case 'dec':
      return count - 1;
    default:
      throw new Error(`${action.type}`);
  }
}
/*
 * dispatch 함수의 동일성이 안정적이고 리렌더링 시에도 변경되지 않는다.
 */
function HookTest() {
  const [count, dispatch] = useReducer(reducerFunc, 0);

  return (
    <div>
      <h1>{count}</h1>
      <button onClick={() => dispatch({ type: 'inc' })}>클릭</button>
    </div>
  );
}

export default HookTest;

Custom Hook

중복된 코드를 공통된 코드로 리팩토링하는 것을 커스텀 훅이라고 합니다.
(⭐️규칙 : 반드시 use로 시작해야 합니다, 리액트에게 이 함수가 커스텀 훅임을 알려야 함)
=> 그래야 내장 훅 사용 가능함
만약 두 컴포넌트에서 data를 받아오는 useEffect 내의 fetch함수 부분이 똑같다면 이를 하나로 통일시키고 싶을때 그럴때 custom hooks를 통해 이를 해결할 수 있습니다.

const useFetch = (url: string) => {
  const [dataList, setDataList] = useState([]);

  useEffect(() => {
    fetch(url)
      .then((res) => res.json())
      .then(setDataList)
      .catch((error) => console.log(error));
  }, [url]);

  return dataList;
};

export function CustomHooks() {
  const dataList = useFetch('https://jsonplaceholder.typicode.com/users');
  console.log(dataList);
  return (
    <div>
      <h1>Custom</h1>
      <TestHooks />
    </div>
  );
}

const id = 1;

function TestHooks() {
  const detailData = useFetch(`https://jsonplaceholder.typicode.com/users/${id}`);
  console.log(detailData);
  return (
    <div>
      <h2>Good</h2>
    </div>
  );
}
profile
{IvanSelah : ["꿈꾸는", "끊임없이 노력하는", "프론트엔드 개발자"]}

0개의 댓글