【问题标题】:Is parent component re-rendered when the child component returns with something new?当子组件返回新内容时,是否会重新渲染父组件?
【发布时间】:2019-03-10 12:51:10
【问题描述】:

据我所知,当父组件的stateprops 更改时,子组件正在重新渲染。

但我不知道反之亦然。

这是一个代码。

usePromise.js(定制的钩子)

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;
}

使用PromiseSample.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;

从上面的代码可以看出,child(usePromise.js) 组件的状态变化了四次。

但似乎 parent(usePromiseSample.js) 也被重新渲染了四次,因为 test 被记录了四次。

我怎样才能轻松理解这种情况?

【问题讨论】:

  • usePromise 不是子组件,而是自定义钩子。钩子本身不会被重新渲染,但使用它的组件会。如果您在另一个组件中渲染 UsePromiseSample,您会看到当 UsePromiseSample 存在时父级没有重新渲染。
  • 我不确定我们是否仍然可以使用带有钩子的 parent / child 词汇。这是依赖关系的问题; usePromiseSample 依赖于usePromise,因此如果usePromise 状态发生变化,它将重新加载。
  • 谢谢@Tholle,但是那个钩子本身不是没有被重新渲染吗?我很困惑,因为如果我们将 console.log() 添加到 UsePromise.js,它也会注销四次。
  • @soywod 感谢您的重要更正。所以我们可以使用父/子这个词来表示类组件,但不能用于功能组件,对吧?
  • 这不是关于类或函数组件,而是关于钩子。这是一种不同的方法。你不能说usePromiseusePromiseSample 的孩子。但是usePromiseSample 使用(或依赖于)usePromise

标签: reactjs react-hooks


【解决方案1】:

usePromise 不是子组件,而是自定义钩子。当在usePromise 中调度操作时,钩子本身不会被重新渲染,但使用它的组件是。

如果你在另一个组件中渲染UsePromiseSample,你会看到当UsePromiseSample是时父级没有重新渲染。

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>

【讨论】:

  • 感谢您对代码的详细解释。但似乎钩子本身正在重新渲染,因为如果我将 console.log('something') 添加到 usePromise.js,它会记录四次'something'。你能解释一下这个问题吗?
  • @jwkoo 钩子本身并没有重新渲染,而是使用它的组件重新渲染。当使用它的组件被重新渲染时,usePromise 钩子将被调用并且其中的任何console.log 都将被运行。我不确定我还能用什么其他方式来解释它。
  • 问这个很傻。你说的对。在调度时重新渲染正在使用自定义钩子的组件。谢谢
猜你喜欢
  • 2018-10-07
  • 2022-08-10
  • 2020-02-13
  • 2018-08-12
  • 2019-06-01
  • 1970-01-01
  • 2021-12-01
  • 1970-01-01
  • 2021-01-24
相关资源
最近更新 更多