Reactjs 异步渲染组件

Posted

技术标签:

【中文标题】Reactjs 异步渲染组件【英文标题】:Reactjs async rendering of components 【发布时间】: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() 进行渲染并抽象出其余部分。此外,您可能只想获取该数据一次,而不是每次渲染组件时。 【参考方案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】:

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

    将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>;
      
    );
    

    将 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 在构造函数中初始化状态

以上是关于Reactjs 异步渲染组件的主要内容,如果未能解决你的问题,请参考以下文章

使用 ReactJS 的 Datatables.net,在一列中渲染一个 ReactJS 组件

AJAX 调用 ReactJS 后渲染组件

ReactJs 上的无限循环渲染组件

Reactjs - 路线中的“组件”与“渲染”

从 ReactJS 外部渲染组件

如何渲染存储在 redux reducer 中的 reactjs 组件?