【问题标题】:Conditonal component still renders - ReactJS条件组件仍然呈现 - React JS
【发布时间】:2018-08-28 08:19:52
【问题描述】:

我正在学习 react 并想尝试创建一个加载组件,该组件显示加载文本,直到满足条件,即道具具有正确的信息。

问题是即使不满足条件,元素仍在加载:

主要成分:

    import React from 'react';

    const Loading = ({ condition, children }) => (<div>{condition ? children :
 'Loading'}</div>);       

    export default Loading;

这是我使用加载组件的组件的渲染方法:

return 
(<Loading condition={props.data && props.data.result && props.data.result.length > 1}> 
<div> { ViewHelper.getCatalogItems(props.data) }</div></Loading>);

现在我的问题是我在调用 { ViewHelper.getCatalogItems(props.data) } 时遇到错误,因为 props.data 是未定义的,但是我希望如果 LoadingComponent 中的三元条件为假,加载组件不会调用该函数。

如果我将 ViewHelper.getData 更改为某个字符串值,一切似乎都可以正常工作并显示“正在加载”。

谢谢

【问题讨论】:

  • ViewHelper.getCatalogItems(props.data) 是应该在那里调用,还是应该使用箭头函数?
  • 我希望它为一张桌子“映射”一堆项目。如果数据可用,我想我想在那里调用它,即加载条件解析为 true
  • 你想渲染结果吗?
  • 仅当数据可用时。所以如果 props.data 不是未定义的,那么我想调用该函数。我希望加载组件会确定是否使用三元来调用它,但它似乎都一样。
  • 问题是你打电话给props.data,不管它是否被定义。你能发布你的getCatalogItems函数吗?

标签: reactjs


【解决方案1】:

Loading 组件不使用children 的事实并不意味着不会渲染子级。否则不会有任何东西可以作为props.children 传递给父组件。

this example 中可以看出,尽管父组件中忽略了children 属性,但仍会评估子表达式。

处理此问题并防止孩子急切渲染的正确方法是使用render prop recipe,也称为function as a child

const Loading = ({ condition, children }) => (
  <div>{condition && children ? children() : 'Loading'}</div>
);       

...

<Loading condition={props.data && props.data.result && props.data.result.length > 1}>
  {() => (
    <div> { ViewHelper.getCatalogItems(props.data) }</div>
  )}
</Loading>

注意,由于props.children是一个函数,所以在三元表达式中它被用作children()

或者对组件使用 HOC:

const withLoading = (Comp) =>
  ({ condition, ...props }) => (
    <div>{condition ? <Comp {...props} /> : 'Loading'}</div>
  )      
);

...

const LoadingCatalogItemsComponent = withLoading(CatalogItemsComponent);

【讨论】:

    【解决方案2】:
    1. 在 Loading 组件中接收到的 children 参数将已经在父组件中呈现(尝试从 Loading 中记录子组件的内容)
    2. 无论条件如何,都会调用 getCatalogItems

    需要在此处处理 props.data 的情况。有多种方法可以做到这一点:

    【讨论】:

    • 我想这样做的真正原因是为了防止自己不得不为页面上的一堆不同控件编写对 props.data 的空检查。我可以添加一个空检查或解构或默认值,但是我必须为每个需要数据的控件继续这样做,然后显示加载等。
    【解决方案3】:

    更新的答案:正如 estus 在下面指出的那样,渲染道具可能是做到这一点的好方法。这是一个例子:

    import React from "react";
    import ReactDOM from "react-dom";
    
    const Loading = ({ condition, render }) => {
      if (condition) {
        return render();
      } else {
        return "Loading";
      }
    };
    
    const Thing = ({ data }) => {
      console.log(data);
      return data.map(d => <li>{d}</li>);
    };
    
    class App extends React.Component {
      state = {
        data: [1, 2, 3]
      };
    
      render() {
        return (
          <Loading
            condition={this.state.data.length > 1}
            render={() => {
              return <Thing data={this.state.data} />;
            }}
          />
        );
      }
    }
    
    const rootElement = document.getElementById("root");
    ReactDOM.render(<App />, rootElement);
    

    代码沙盒here.

    【讨论】:

    • 我试过了,但仍然得到未定义的错误。为什么克隆子组件更好?就像我说的那样,我对此真的很陌生:/
    【解决方案4】:

    作为@estus said,HOC 和渲染道具是两种流行的方法。搬家:

    <div>{ ViewHelper.getCatalogItems(props.data)}</div>
    

    到它自己的组件中(它是传递道具,例如,&lt;CatalogItems {...props} /&gt;)也会阻止你得到错误。只要代码不在实际的渲染方法中;否则,无论 React 是否实际渲染它,它都会被触发。

    例子:

    const Loading = ({ condition, children }) => (
      <div>{condition ? children : "Loading in 3 seconds"}</div>
    );
    
    // now that it's in its own component the code isn't run until the component actually renders
    const CatalogItems = ({ data }) => data.result.map(item => item);
    
    class App extends React.Component {
      state = {
        data: null
      };
    
      // dummy API call
      componentDidMount() {
        setTimeout(
          () => this.setState({ data: { result: ["cat ", "dog ", "mouse "] } }),
          3000
        );
      }
    
      render() {
        const props = this.state; // let's just pretend these were inherited props
    
        return (
          <Loading
            condition={
              props.data && props.data.result && props.data.result.length > 1
            }
            data={props.data}
          >
            <CatalogItems {...props} />
          </Loading>
        );
      }
    }
    
    const rootElement = document.getElementById("root");
    ReactDOM.render(<App />, rootElement);
    <div id='root'></div>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.3.1/umd/react.development.js"></script>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.3.1/umd/react-dom.development.js"></script>

    【讨论】:

    • 如果您不想传递所有道具,也可以直接使用&lt;CatalogItems data={props.data} /&gt;
    猜你喜欢
    • 2017-06-25
    • 2019-09-13
    • 2013-03-30
    • 2021-02-17
    • 2017-11-11
    • 1970-01-01
    相关资源
    最近更新 更多