inflearn logo
강의

Course

Instructor

[React Part 2] Advanced Topics and Hooks

[Chapter 3.5 Context Hook] 3.5.2 useContenxt

[3.5장 컨택스트 훅] 3.5.2 useContenxt 에서 질문이 있습니다.

Resolved

432

dohyunlim

67 asked

1

안녕하세요 선생님 Count, PlusButton이 re render 되는 조건을 알고 싶습니다.

 

예전예시에서는

    class Consumer extends React.Component {
      constructor(props) {
        super(props);
        this.state = {
          value: emitter.get(),
        };
        this.setValue = this.setValue.bind(this);
      }

      setValue(nextValue) {
        this.setState({ value: nextValue });
      }

      componentDidMount() {
        emitter.on(this.setValue);
      }

      componentWillUnmount() {
        emitter.off(this.setValue);
      }
      render() {
        return <>{this.props.children(this.state.value)}</>;
      }
    }

Consumer가 state를 가지고 있음으로

순서가

Provider render -> Consumer render -> Consumer componentDidMount -> Provider componentDidMount (set 을 통해 빈 객체였던 것을 value, setValue로 바꿔줌)

이때 Consumer state는 emitter.get()임으로 변경된 것을 감지 하고 re render 하는 것으로 이해했었습니다.

헌데 이번예시에서는

  function useContext(context) {
    console.log("userContext, context.emitter.get() = ", context.emitter.get());
    const [value, setValue] = React.useState(context.emitter.get());

    React.useEffect(() => {
      console.log("Consumer useEffect");
      context.emitter.on(setValue);
      return () => {
        console.log("Consumer useEffect clean");
        context.emitter.off(setValue);
      };
    }, [context]);

    return value;
  } 
const Count = () => {
  console.log("Count render");
  const { count } = MyReact.useContext(countContext);
  return <div>{count}</div>;
};



Provider render -> Count(Consumer) render -> Count's useEffect -> Provider's useEffect 을 통해
emitter 값이 빈객체에서 count, setCount로 채워지는것은 이해하였습니다.

이때 Count 가 다시 한번 re render되는데 왜 그런 것인가요?

첫번째 예시처럼 state?같은게 존재하는건가요?
다시 render되는 조건이 궁금합니다.

react React-Context react-hooks react-router react-component

Answer 2

1

dohyunlim

제가 질문을 엉뚱하게 드린 것 같습니다.

class component의 경우 자신의 state 변수가 변경이 일어나면 해당 class component와 child들이 순서대로 rendering이 다시 일어나게 되는데

  function useContext(context) {
    console.log("userContext, context.emitter.get() = ", context.emitter.get());
    const [value, setValue] = React.useState(context.emitter.get());

    React.useEffect(() => {
      console.log("Consumer useEffect");
      context.emitter.on(setValue);
      return () => {
        console.log("Consumer useEffect clean");
        context.emitter.off(setValue);
      };
    }, [context]);

    return value;
  }

useContext function component 에서 사용된 useState의 state가 변경이 되는 경우를 살펴보면

const Count = () => {
  console.log("Count render");
  const { count } = MyReact.useContext(countContext);
  return <div>{count}</div>;
};

imageProvider의 useEffect 의 emitter set을 통해 emitter 값이 빈객체에서 제대로 채워지게 되는데

이때 변경된 것은 useContext의 state인데 왜 Count가 re render되는지 궁금했습니다.


정리하면 함수 컴포넌트에서 state가 변경될 때 re render되는 범위 및 순서가 궁금하였습니다.

1

jeonghwan

useContext가 제공하는 상태를 Count 컴포넌트가 사용하기 때문입니다.

const { count } = MyReact.useContext(countContext);

상태훅에서 다루긴했는데요. 상태가 바귀면 이를 사용한 컴포넌트도 다시 그려주는 것이 리액트 상태 훅의 역할이었습니다.

MyReact.useContext도 상태훅 기반으로 실습했는데요. 이 상태를 제공하는 커스텀 훅인 셈이에요. 이 상태가 바뀌면 이를 사용하는 측에서도 렌더링되는 겁니다.

1

jeonghwan

MyReact.useContext() 때문입니다.

Count에서 처음 호출될때 처음 컨택스트 값을 가져옵니다.

const [value, setValue] = React.useState(context.emitter.get());

아직 프로바이더가 값이 제공하기 전이라 폴백으로으로 전달한 빈 객체가 전달 될거에요. Count는 이 컨택스트에서 값을 찾기 때문에 undefined로 처음 렌더합니다.

useContext는 context값에 따라 부수효과를 실행하는데요. 프로바이더가 제공한 context 값이 바뀌어 동작합니다. 컨택스트에게 값이 바뀌면 setValue를 호출하도록 요청하고. 컨택스트가 이 함수를 실행하면 상태가 바뀌면서 다시한번 value가 바뀌게 됩니다. Count가 두번째 레더합니다.

실제 리액트의 훅은 번만 렌더하는데요. 마이리액트는 리액트 동작 이해를 위한 용도로 봐주시면 좋겠습니다.

1

jeonghwan

기존 실습 코드에서 두 번 렌더되는 현상을 해결한 코드입니다.

createContext의 인자를 컨택스트의 폴백(fallback) 값으로 사용합니다. 기존에는 이 값으로 에미터를 초기화 했습니다. 그리고 props.value로 에미터의 값을 바꾸는 방식이었습니다. 이것을 에미터의 상태를 바꾸고 구독 함수를 두 번 호출해 리렌더까지 영향주는 원인이었습니다.

  // 인자이름을 defaultValue로 바꾸었습니다.
  // 프로바이더 컴포넌트로 감싸지 않아 값을 주입하지 못할 경우 기본값으로 사용되기 때문입니다.
  function createContext(defaultValue) {
    // 에미터 생성을 늦춥니다.
    // 기존에는 defaultValue로 객체를 생성했습니다. (문제 원인)
    let emitter;

    function Provider({ value, children }) {
      // 에미터 객체를 props.value로 생성합니다.
      if (!emitter) {
        emitter = createEventEmitter(value);
      }

      React.useEffect(() => {
        emitter.set(value);
      }, [value]);

      return <>{children}</>;
    }

    // 컨택스트 값을 조회합니다.
    // 에미터에서 조회하고 프로바이더로 감싸지 않아 값을 제공받지 못하면 지정한 기본 값을 반환합니다.
    function getValue() {
      return emitter ? emitter.get() : defaultValue;
    }

    // 컨택스트 값 변화를 구독합니다.
    // 기존에는 emitter 객체를 노출했지만 undefined일 경우가 있어 방어 로직이 있는 함수를 정의했습니다.
    function on(handler) {
      if (emitter) {
        emitter.on(handler);
      }
    }

    // 컨택스트 값 변화를 구독 취소합니다.
    // 기존에는 emitter 객체를 노출했지만 undefined일 경우가 있어 방어 로직이 있는 함수를 정의했습니다.
    function off(handler) {
      if (emitter) {
        emitter.off(handler);
      }
    }

    return {
      Provider,
      getValue,
      on,
      off,
    };
  }

이를 사용한 useContext 훅도 변경했습니다. 폴백값이 아닌 프롭스를 통해 들어온 컨택스트 값을 이용해 상태를 만들었습니다. 컨택스트가 바뀌면 이 상태도 업데이트하여 훅을 사용하는 컴포넌트가 다시 그려지도록 의도한 코드입니다.

  function useContext(context) {
    // 컨택스트 값으로 상태를 정의합니다.
    const [value, setValue] = React.useState(context.getValue());

    React.useEffect(() => {
      // 컨택스트 값을 구독합니다.
      // 값이 바뀌면 value를 변경해 이 훅을 사용하는 컴포넌트를 다시 그립니다.
      context.on(setValue);

      return () => {
        // 컨택스트 구독을 취소합니다.
        context.off(setValue);
      };
    }, [context]);

    return value;
  }

잘못된 useEffect 사용?

1

65

2

useEffect 의존성 질문

1

57

2

orderableProductItem 에 관하여...

0

56

2

강의 자료, 블로그, 깃 주소

0

63

1

React 훅 구현 원리와 실무 패턴 관련 질문 (useState, useEffect 순서 및 핸들러 구조)

1

104

2

pushState로 주소를 바꾸면 렌더링이 안 되는 이유가 궁금합니다.

1

87

2

FormControl 컴포넌트 사용시 htmlFor prop 값 넘길 때 중괄호 이유

1

123

1

dispatch 함수도 리렌더링 유발하지 않나요?

1

203

2

ProductItem에서 onClick = {onClick}을 달지 않아도 되는 이유

1

160

2

replaceState를 쓰지 않는 대안

1

195

2

setValue 메서드를 바인딩 해야 하는 이유

1

163

2

MyReact를 IIFE(즉시실행함수)로 설계하신 이유

0

208

2

[4.4장 메모이제이션 훅] 4.4.3 memo 참조 비교

0

129

1

useRef 관련하여 질문드립니다

0

152

2

렌더 프롭 관련하여 질문드립니다

0

182

2

[1.2장 상품목록 화면] 1.2.3 Button ...rest 관련 질문 드립니다.

1

395

2

[4.4장 메모이제이션 훅] 4.4.4 useCallback curried function에 관한 질문입니다.

1

315

2

[4.4장 메모이제이션 훅] 4.4.2 useMemo 에서 every 함수에 관한 질문입니다.

1

355

2

[4.3장 리듀서 훅] 4.3.7 활용 MyForm(풀이) 오타 제보 및 질문이 있습니다.

1

281

2

[4.1장 레프 훅] useRef관련 질문이있습니다.

1

334

1

[1.3장 주문 내역 화면] 1.3.5 Card 조합 질문 있습니다

2

283

1

[1.3.6 상태정의] state 관련

1

259

1

[2.4장 다이얼로그 1] 2.4.5 withLayout(풀이) 에 관한 질문입니다.

1

272

1

[2.1장 컨택스트] 2.1.4 공급자와 소비자 / 에서 질문이 있습니다.

1

424

2