【问题标题】:React higher-order component - Warning: Cannot update a component from inside the function body of a different component反应高阶组件 - 警告:无法从不同组件的函数体内更新组件
【发布时间】:2020-08-05 23:37:56
【问题描述】:

我制作了一个非常简单的高阶组件,它从 API 端点获取数据,并显示加载微调器、失败时的错误消息或成功时的组件。

组件工作正常,但我收到以下错误消息:

Warning: Cannot update a component from inside the function body of a different component. in Unknown (at ApiData.tsx:34) ok: value => <WrappedComponent data={value} />

通过阅读类似的帖子,我了解到 React 在显示此消息时可能有点过分热心。我使用的是完全不可变的 Redux 存储,并且不使用任何非功能组件。我担心因为我收到此警告,如果我稍后尝试扩展此组件(例如,通过每隔一段时间自动刷新 API)更新将无法正确触发。另外,这是一条警告信息 - 我们应该注意 ;)

以下是 HOC 的代码

import React, { ComponentType } from 'react'
import { IResult } from 'typescript-monads'
import { useDispatch } from '../../store'
import { PayloadActionCreator } from 'typesafe-actions'
import { Preloader, PreloaderSize } from 'react-materialize'
import styles from './ApiData.module.scss'

interface ApiProps<TKey, TData> {
  key: TKey
  data?: IResult<TData, Error> | "loading"
  get: { request: PayloadActionCreator<string, TKey> }
  loaderSize?: PreloaderSize | "inline"
}

export interface DataProps<TData> {
  data: TData
}

export default <TKey, TData>({ key, data, get, loaderSize }: ApiProps<TKey, TData>) =>
  (WrappedComponent: ComponentType<DataProps<TData>>) => {
    const dispatch = useDispatch()
    if (!data) {
      dispatch(get.request(key))
      return <span>&nbsp;</span>
    } else if (data === "loading") {
      if (!loaderSize || loaderSize === "inline") {
        return <span><Preloader className={styles.preloader} /></span>
      } else {
        return <Preloader size={loaderSize} />
      }
    } else {
      return data.match({
        fail: error => <span>An error occurred: {error.message}</span>,
        ok: value => <WrappedComponent data={value} />
      })
    }
  }

这是一个如何使用它的示例:

const somePart = ApiData<SomeObjectKey, SomeObject>({...})
...
return <div>{somePart(SomeComponent)}</div>

谁能建议为什么会提出这个警告,以及如何删除/解决?谢谢。

编辑:有人建议 data.match 可能是一个问题 - 我也尝试了以下方法,但仍然出错:

if (data.isFail()) {
  return <span>An error occurred: {data.unwrapFail()}</span>
} else {
  return <WrappedComponent data={data.unwrap()} />
}

【问题讨论】:

  • data.match 是调用函数,还是只是返回函数?调用该函数将是一个问题。
  • data.match 是一个函数,但我在这里尝试了两种不同的返回值的方式 - 查看没有函数调用的编辑

标签: reactjs typescript react-redux react-functional-component


【解决方案1】:

我注意到您的参数顺序可能有误。通常,对于 HOC,您希望首先获取包装的组件,然后返回一个函数,该函数采用 props 并以某种方式返回涉及包装组件的 JSX。

// next two lines have been swapped
export default (WrappedComponent: ComponentType<DataProps<TData>>) => { 
  return ({ key, data, get, loaderSize }: ApiProps<TKey, TData>) => {
    const dispatch = useDispatch()
    if (!data) {
      dispatch(get.request(key))
      return <span>&nbsp;</span>
    } else if (data === "loading") {
      if (!loaderSize || loaderSize === "inline") {
        return <span><Preloader className={styles.preloader} /></span>
      } else {
        return <Preloader size={loaderSize} />
      }
    } else {
      if (data.isFail()) {
        return <span>An error occurred: {data.unwrapFail()}</span>
      } else {
        return <WrappedComponent data={data.unwrap()} />
      }
    }
  }
}

现在您可以像这样使用您的 HOC:

import HOC from "./hoc-file"

function OriginalComponent(/* ... */) { /* ... */ }

const WrappedComponent = HOC(OriginalComponent);

ReactDOM.render(<WrappedComponent {...props} />, rootDomElement);

【讨论】:

  • 在线查看有关 HOC 的各种资源时,我发现了这两种变体。我选择了我所做的顺序,因为我可以部分应用 API 数据规范,然后将它传递给不同的组件。我将尝试交换订单,看看警告是否消失。
  • 我担心的是 React 需要 (props) =&gt; JSX 才能将函数作为组件,而另一种样式返回 (component) =&gt; JSX,React 不知道如何处理。
  • 很抱歉消失了几个星期 - 其他优先事项!我现在明白你的意思了——当我回到这个项目并开始引入可编辑控件时,我注意到 React 正在重新渲染 HOC 下的所有内容,因为它“不知道如何处理”它。我将尝试颠倒参数并按照您现在的建议使用它们。
猜你喜欢
  • 2020-06-16
  • 2021-02-06
  • 1970-01-01
  • 1970-01-01
  • 2022-01-13
  • 1970-01-01
  • 1970-01-01
  • 2020-11-06
  • 2020-11-08
相关资源
最近更新 更多