React 공식 문서 읽어보기 (4)

Snoop So·2023년 8월 14일
0

useContext

  • 컴포넌트에서 context를 읽고 구독할 수 있게 해주는 React Hook

  • useContext는 호출하는 컴포넌트에 대한 context 값을 반환함

  • 위 값은 호출한 컴포넌트에서 트리상 가장 가까운 SomeContext.Provider에 전달된 value

  • 이런 provider가 없을 경우에는 반환되는 값은 createContext에 전달한 defaultValue가 됨

  • 동일한 컴포넌트에서 반환된 provider의 영향을 받지 않음을 주의

  • context를 업데이트 하려면?

function MyPage() {
  const [theme, setTheme] = useState('dark');
  return (
    <ThemeContext.Provider value={theme}>
      <Form />
      <Button onClick={() => {
        setTheme('light');
      }}>
        Switch to light theme
      </Button>
    </ThemeContext.Provider>
  );
}
  • 필요한 만큼 provider를 중첩하고 재정의할 수 있다
import { createContext, useContext } from 'react';

const ThemeContext = createContext(null);

export default function MyApp() {
  return (
    <ThemeContext.Provider value="dark">
      <Form />
    </ThemeContext.Provider>
  )
}

function Form() {
  return (
    <Panel title="Welcome">
      <Button>Sign up</Button>
      <Button>Log in</Button>
      <ThemeContext.Provider value="light">
        <Footer />
      </ThemeContext.Provider>
    </Panel>
  );
}

function Footer() {
  return (
    <footer>
      <Button>Settings</Button>
    </footer>
  );
}

function Panel({ title, children }) {
  const theme = useContext(ThemeContext);
  const className = 'panel-' + theme;
  return (
    <section className={className}>
      {title && <h1>{title}</h1>}
      {children}
    </section>
  )
}

function Button({ children }) {
  const theme = useContext(ThemeContext);
  const className = 'button-' + theme;
  return (
    <button className={className}>
      {children}
    </button>
  );
}
  • 이런식으로 값을 누적해서 사용할 수도 있음
import { useContext } from 'react';
import { LevelContext } from './LevelContext.js';

export default function Section({ children }) {
  const level = useContext(LevelContext);
  return (
    <section className="section">
      <LevelContext.Provider value={level + 1}>
        {children}
      </LevelContext.Provider>
    </section>
  );
}
  • 렌더링 최적화를 위해 useMemo, useCallback으로 감싸주기
import { useCallback, useMemo } from 'react';

function MyApp() {
  const [currentUser, setCurrentUser] = useState(null);

  const login = useCallback((response) => {
    storeCredentials(response.credentials);
    setCurrentUser(response.user);
  }, []);

  const contextValue = useMemo(() => ({
    currentUser,
    login
  }), [currentUser, login]);

  return (
    <AuthContext.Provider value={contextValue}>
      <Page />
    </AuthContext.Provider>
  );
}

useRef

  • 컴포넌트 최상위 레벨에서 useRef를 호출, ref 선언
  • 초기화를 제외하고는 렌더링 중에 ref.current를 쓰거나 읽지 말 것. 동작을 예측하기 어려워짐
function MyComponent() {
  // ...
  // 🚩 Don't write a ref during rendering
  // 🚩 렌더링 중에 ref를 작성하지 마세요.
  myRef.current = 123;
  // ...
  // 🚩 Don't read a ref during rendering
  // 🚩 렌더링 중에 ref를 읽지 마세요.
  return <h1>{myOtherRef.current}</h1>;
}
function MyComponent() {
  // ...
  useEffect(() => {
    // ✅ You can read or write refs in effects
    // ✅ Effect에서 ref를 읽거나 쓸 수 있습니다.
    myRef.current = 123;
  });
  // ...
  function handleClick() {
    // ✅ You can read or write refs in event handlers
    // ✅ 이벤트 핸들러에서 ref를 읽거나 쓸 수 있습니다.
    doSomething(myOtherRef.current);
  }
  // ...
}
  • ref를 변경해도 리렌더링을 촉발하지 않음
  • 정보가 공유되는 외부 변수와는 달리 각각의 컴포넌트에 로컬로 저장됨.
  • 스크롤을 특정 element의 중앙값으로 이동시키는 코드
import { useRef } from 'react';

export default function CatFriends() {
  const listRef = useRef(null);

  function scrollToIndex(index) {
    const listNode = listRef.current;
    // This line assumes a particular DOM structure:
    // 다음 코드는 특정 DOM 구조를 가정합니다.
    const imgNode = listNode.querySelectorAll('li > img')[index];
    imgNode.scrollIntoView({
      behavior: 'smooth',
      block: 'nearest',
      inline: 'center'
    });
  }

  return (
    <>
      <nav>
        <button onClick={() => scrollToIndex(0)}>
          Tom
        </button>
        <button onClick={() => scrollToIndex(1)}>
          Maru
        </button>
        <button onClick={() => scrollToIndex(2)}>
          Jellylorum
        </button>
      </nav>
      <div>
        <ul ref={listRef}>
          <li>
            <img
              src="https://placekitten.com/g/200/200"
              alt="Tom"
            />
          </li>
          <li>
            <img
              src="https://placekitten.com/g/300/200"
              alt="Maru"
            />
          </li>
          <li>
            <img
              src="https://placekitten.com/g/250/200"
              alt="Jellylorum"
            />
          </li>
        </ul>
      </div>
    </>
  );
}

https://codesandbox.io/s/41fboj?file=%2FApp.js&utm_medium=sandpack
위 코드 예제를 한번 만들어보면 좋을듯.

function Video() {
  const playerRef = useRef(new VideoPlayer());
  // ...

위와 같이 했을때 불필요한 렌더링 발생할 수 있음.
ref 초기화를 다음과 같이 하기

function Video() {
  const playerRef = useRef(null);
  if (playerRef.current === null) {
    playerRef.current = new VideoPlayer();
  }
  // ...

0개의 댓글