首页 > 解决方案 > React:组件的抽象。合成与渲染道具

问题描述

长问题请准备好

我最近一直在使用react-query,发现每个使用useQuery. 例如:

if(query.isLoading) {
   return 'Loading..'
}

if(query.isError) {
  return 'Error'
}

if(query.isSuccess) {
   return 'YOUR ACTUAL COMPONENT'
}

我尝试创建一个包装器组件,您将查询信息传递给该包装器组件,它将为您处理所有状态。

一个基本的实现如下:

const Wrapper = ({ query, LoadingComponent, ErrorComponent, children }) => {

  if (query.isLoading) {
    const toRender = LoadingComponent || <DefaultLoader />;
    return <div className="grid place-items-center">{toRender}</div>;
  }

  if (query.isError) {
    const toRender = ErrorComponent ? (
      <ErrorComponent />
    ) : (
      <div className="dark:text-white">Failed to Load</div>
    );
    return <div className="grid place-items-center">{toRender}</div>;
  }

  if (query.isSuccess) {
    return React.Children.only(children);
  }
  return null;
};

并且,像这样使用它:

const Main = () => {
   const query = useQuery(...);
   return (
      <Wrapper query={query}>
         <div>{query.message}</div>
      </Wrapper>

   )
}

问题在于它query.message可能是未定义的,因此会引发错误。 无法读取未定义的属性消息

但是,它应该可以通过可选链接来修复query?.message

这就是我的困惑出现的地方。包装器内的 UI 元素应该已经渲染,但它们没有。而且,如果他们不这样做,那为什么会引发错误。

这意味着,chilren包装器在每次渲染时执行。但不可见。为什么??

渲染道具进行救援

包装器

  const WrapperWithRP = ({ query, children }) => {
      const { isLoading, data, isError, error } = query;
      if (isLoading) return 'Loading...'
      if (isError) return 'Error: ' + error

      return children(data)
  }

并像使用它一样,

const Main = () => {
   const query = useQuery(...);
   return (
      <Wrapper query={query}>
        {state => {
             return (
                <div>{state.message}</div>
             )
          }
        }
      </Wrapper>

   )
}

这按预期工作。loading并且仅当状态为 not或时才呈现子级error

我仍然对为什么第一种方法在明确执行时不显示 UI 事件中的元素感到困惑?

复制行为的 Codesandbox 链接: https ://codesandbox.io/s/react-composition-and-render-prop-tyyzh?file=/src/App.js

标签: javascriptreactjsreact-nativewebreact-query

解决方案


好的,所以我想通了,它比听起来简单。

在方法一中:JSX 仍然会被执行,因为它是一个函数调用React.createElement。但是,Wrapper只有在查询成功时才返回孩子。因此,子项在 UI 上将不可见。

两种方法都有效,但使用方法 1,我们可能不得不处理undefined数据。

我最终使用 render-prop 作为我的解决方案,因为它看起来更干净。


推荐阅读