jwkoo
jwkoo

Reputation: 2663

Is parent component re-rendered when the child component returns with something new?

As long as I know, child component is being re-rendered when the parent component's state or props change.

But I have no idea with the case of vice-versa.

Here is a code.

usePromise.js (custom made hooks)

import { useEffect, useReducer } from 'react';

const reducer = (state, action) => {
    switch (action.type) {
        case 'RESOLVED':
            return { ...state, resolved: action.diff };
        case 'LOADING':
            return { ...state, loading: action.diff };
        case 'ERROR':
            return { ...state, resolved: action.diff };
        default:
            return state;
    }
};

export default function usePromise(promiseCreator, deps = []) {
    const [state, dispatch] = useReducer(reducer, {
        resolved: null,
        loading: false,
        error: null
    });

    const process = async () => {
        dispatch({ type: 'LOADING', diff: true });
        try {
            const result = await promiseCreator();
            dispatch({ type: 'RESOLVED', diff: result });
        } catch (e) {
            dispatch({ type: 'ERROR', diff: e });
        }
        dispatch({ type: 'LOADING', diff: false });
    };

    useEffect(() => {
        process();
    }, deps);

    return state;
}

usePromiseSample.js

import React from 'react';
import usePromise from './usePromise';

const wait = () => {
    return new Promise(resolve =>
        setTimeout(() => resolve('Hello hooks!'), 3000)
    );
};

const UsePromiseSample = () => {
    const { resolved, loading, error } = usePromise(wait);

    console.log('test')

    if (loading) return <div>loading...</div>;
    if (error) return <div>error happened!</div>;
    if (!resolved) return null;

    return <div>{resolved}</div>;
};

export default UsePromiseSample;

As you can see above the code, child(usePromise.js) component's state is changing four times.

But it seems that parent(usePromiseSample.js) is also being re-rendered four times since test is logged four times.

How can I understand this situation easily?

Upvotes: 5

Views: 455

Answers (1)

Tholle
Tholle

Reputation: 112927

usePromise is not a child component, but a custom hook. The hook itself it not being re-rendered when an action is dispatched inside usePromise, but the component that uses it is.

If you render UsePromiseSample inside another component, you will see that the parent is not re-rendering when UsePromiseSample is.

const { useEffect, useReducer } = React;

const reducer = (state, action) => {
  switch (action.type) {
    case 'RESOLVED':
      return { ...state, resolved: action.diff, loading: false };
    case 'ERROR':
      return { ...state, resolved: action.diff, loading: false };
    default:
      return state;
  }
};

function usePromise(promiseCreator, deps = []) {
  const [state, dispatch] = useReducer(reducer, {
    resolved: null,
    loading: true,
    error: null
  });

  const process = () => {
     promiseCreator()
       .then(result => {
         dispatch({ type: 'RESOLVED', diff: result });
       })
       .catch(e => {
        dispatch({ type: 'ERROR', diff: e });
       });
  };

  useEffect(() => {
    process();
  }, deps);

  return state;
}

const wait = () => {
  return new Promise(resolve =>
    setTimeout(() => resolve('Hello hooks!'), 3000)
  );
};

const UsePromiseSample = () => {
  const { resolved, loading, error } = usePromise(wait);

  console.log('UsePromiseSample rendered')

  if (loading) return <div>loading...</div>;
  if (error) return <div>error happened!</div>;
  if (!resolved) return null;

  return <div>{resolved}</div>;
};

const App = () => {
  console.log('App rendered')

  return <UsePromiseSample />
}

ReactDOM.render(<App />, document.getElementById("root"));
<script src="https://unpkg.com/react@16/umd/react.development.js"></script>
<script src="https://unpkg.com/react-dom@16/umd/react-dom.development.js"></script>

<div id="root"></div>

Upvotes: 1

Related Questions