【问题标题】:Reactjs async rendering of componentsReactjs 异步渲染组件
【发布时间】:2015-01-27 08:19:35
【问题描述】:

我想在我的 ajax 请求完成后渲染我的组件。

下面你可以看到我的代码

var CategoriesSetup = React.createClass({

    render: function(){
        var rows = [];
        $.get('http://foobar.io/api/v1/listings/categories/').done(function (data) {
            $.each(data, function(index, element){
                rows.push(<OptionRow obj={element} />);
            });
           return (<Input type='select'>{rows}</Input>)

        })

    }
});

但我收到以下错误,因为我在我的 ajax 请求的 done 方法中返回渲染。

Uncaught Error: Invariant Violation: CategoriesSetup.render(): A valid ReactComponent must be returned. You may have returned undefined, an array or some other invalid object.

有没有办法在开始渲染之前等待我的 ajax 请求结束?

【问题讨论】:

  • 另外,一个小小的挑剔但在 render() 例程中进行数据检索并不是一个好主意。保持 render() 进行渲染并抽象出其余部分。此外,您可能只想获取该数据一次,而不是每次渲染组件时。

标签: javascript ajax asynchronous reactjs jquery-deferred


【解决方案1】:

异步状态管理 (Playground)

以下解决方案允许异步状态管理,如果实施正确,可用于 HTTP 相关要求。

要求

  • 仅重新渲染消耗 observable 的元素。
  • 自动订阅和取消订阅 observable。
  • 支持多个和联合 observables。
  • 提供加载状态
  • 简单易行

预期行为

return (
    <Async select={[names$]}>
        {result => <div>{result}</div>}
    </Async>
);

上面提供的示例将订阅可观察的names$Async 组件的内容/子组件将在可观察对象上触发 next 时重新渲染,不会导致当前组件重新渲染。

异步组件

export type AsyncProps<T extends any[]> = { select: { [K in keyof T]: Observable<T[K]> }, children: (result?: any[]) => JSX.Element };
export type AsyncState = { result?: any[] };

export default class Async<T extends any[]> extends Component<AsyncProps<T>, AsyncState> {

    private subscription!: Subscription;

    constructor(props: AsyncProps<T>) {
        super(props);
        this.state = {};
    }

    componentDidMount() {
        this.subscription = combineLatest(this.props.select)
            .subscribe(result => this.setState({ result: result as T }))
    }

    componentWillUnmount() {
        this.subscription.unsubscribe();
    }

    render() {
        return (
            <Fragment>
                {this.props.children(this.state.result)}
            </Fragment>
        );
    }

}

【讨论】:

    【解决方案2】:

    组件异步渲染的基本示例如下:

    import React                from 'react';
    import ReactDOM             from 'react-dom';        
    import PropTypes            from 'prop-types';
    
    export default class YourComponent extends React.PureComponent {
        constructor(props){
            super(props);
            this.state = {
                data: null
            }       
        }
    
        componentDidMount(){
            const data = {
                    optPost: 'userToStat01',
                    message: 'We make a research of fetch'
                };
            const endpoint = 'http://example.com/api/phpGetPost.php';       
            const setState = this.setState.bind(this);      
            fetch(endpoint, {
                method: 'POST',
                body: JSON.stringify(data)
            })
            .then((resp) => resp.json())
            .then(function(response) {
                setState({data: response.message});
            });
        }
    
        render(){
            return (<div>
                {this.state.data === null ? 
                    <div>Loading</div>
                :
                    <div>{this.state.data}</div>
                }
            </div>);
        }
    }
    

    【讨论】:

      【解决方案3】:

      有两种方法可以处理这个问题,你选择哪种取决于哪个组件应该拥有数据和加载状态。

      1. 将Ajax请求移入父级并有条件地渲染组件:

        var Parent = React.createClass({
          getInitialState: function() {
            return { data: null };
          },
        
          componentDidMount: function() {
            $.get('http://foobar.io/api/v1/listings/categories/').done(function(data) {
              this.setState({data: data});
            }.bind(this));
          },
        
          render: function() {
            if (this.state.data) {
              return <CategoriesSetup data={this.state.data} />;
            }
        
            return <div>Loading...</div>;
          }
        });
        
      2. 将 Ajax 请求保留在组件中,并在加载时有条件地呈现其他内容:

        var CategoriesSetup = React.createClass({
          getInitialState: function() {
            return { data: null };
          },
        
          componentDidMount: function() {
            $.get('http://foobar.io/api/v1/listings/categories/').done(function(data) {
              this.setState({data: data});
            }.bind(this));
          },
        
          render: function() {
            if (this.state.data) {
              return <Input type="select">{this.state.data.map(this.renderRow)}</Input>;
            }
        
            return <div>Loading...</div>;
          },
        
          renderRow: function(row) {
            return <OptionRow obj={row} />;
          }
        });
        

      【讨论】:

      • 我在 2017 年偶然发现了这个答案,这两个最好的解决方案仍然是最好用的吗?
      • @Dan React 基于 props 和 state 渲染 UI,因此核心概念将保持不变——执行 Ajax 请求,设置一些状态,然后重新渲染一些东西。然而,像高阶组件这样的模式变得越来越流行,展示了如何抽象出复杂性。
      • if (this.state.data) 应该是if (this.state &amp;&amp; this.state.data),因为有时状态可能为空。
      • @Timo Hm,在什么情况下this.state 会是null
      • @Timo 在构造函数中初始化状态
      猜你喜欢
      • 2019-02-15
      • 2020-09-04
      • 1970-01-01
      • 1970-01-01
      • 2019-07-21
      • 2018-07-20
      • 2023-04-10
      • 1970-01-01
      相关资源
      最近更新 更多