Reactjs组件的异步渲染

90

我希望在我的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方法中返回render。

Uncaught Error: Invariant Violation: CategoriesSetup.render(): 必须返回一个有效的React组件。您可能已经返回了undefined、一个数组或其他无效的对象。

有没有一种方法可以等待我的ajax请求结束后再开始渲染?


3
还有一点小问题,就是在render()函数中进行数据检索不是个好主意。应该将render()专注于渲染,将其余部分抽象出来。此外,你可能只想获取一次数据,而不是在每次组件渲染时都获取数据。 - Phil Cooper
3个回答

138

有两种处理方法,你需要根据哪个组件应该拥有数据和加载状态来选择。

  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请求放在组件中,并在其加载时有条件地呈现其他内容:

  3. 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} />;
      }
    });
    

6
我在2017年偶然看到了这个答案,那么这两种最佳解决方案现在仍然是最好的选择吗? - Dan
1
应该将 if (this.state.data) 改为 if (this.state && this.state.data),因为有时候状态可能为空。 - Timo Ernst
@Timo Hm,在什么情况下this.state会是null - Michelle Tilley
我制作了这个小助手组件。它可以减轻展示加载状态、进行异步请求和用户事件等方面的痛苦。我在我的最新项目中经常使用它。http://www.anamuser.com/2017/03/26/react-asynccontainer/ - AnAmuser
6
@Timo在构造函数中初始化状态。 - Louis
显示剩余6条评论

7

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

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

0

异步状态管理 (Playground)

以下解决方案允许进行异步状态管理,并且如果正确实现,可用于HTTP相关需求。

要求

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

期望行为

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

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

异步组件

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

}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接