如何在渲染ReactJS组件之前确保通过AJAX加载数据?

时间:2017-02-22 09:24:53

标签: javascript ajax reactjs

我有一个名为UsersTable的父组件(它是其他组件的子组件,并且usersroles作为其道具)。 getRoles()函数使用ajax请求获取用户的所有角色。结果返回render()并存储在allroles变量中。问题是因为Ajax是异步的,我并不总是在allroles变量中得到我需要的值。它有时为空或为所有用户返回相同的值。我想我应该根据描述以某种方式使用componentDidMount和componentWillReceiveProps。但我无法理解。另外,我认为allroles应该是一个状态,但这样做会使代码处于无限循环中,浏览器会一直调用ajax请求!

这是父组件代码:

export const UsersTable = React.createClass({
    getRoles(){
        var oneRole = "";
        this.props.users.forEach(function(user){
            server.getUserRoles(user.id,          
                (results) => {
                    this.oneRole =results['hits']['hits']
                    notifications.success("Get was successful ");
                },
                () => {
                    notifications.danger("get failed ");
                });  
            }.bind(this));
        return this.oneRole;
    },

    render() {
        var rows = [];
        var allroles = this.getRoles()
        this.props.users.map(function(user) {
            rows.push( <UserRow userID={user.id} 
                                userEmail={user.email} 
                                userRoles={allroles} 
                                roles={this.props.roles} />); 
            }.bind(this)); 
        return (
            <table className="table">
                <thead>
                    <tr>
                        <th>Email Address</th>
                        <th>Role</th>
                        <th>Edit</th>
                    </tr>
                </thead>
                <tbody>{rows}</tbody>
            </table>
        );
    }    
});

这是子组件代码:

export const UserRow = React.createClass({
    var showRoles = this.props.userRoles.map((role) => <div> {role.description || ''}</div>);
    render(){
        return (
            <tr>
                <td>{this.props.userEmail}</td>
                <td>{showRoles}</td>
            </tr>
        );
    }
});

2 个答案:

答案 0 :(得分:0)

你说你应该使用生命周期方法是正确的。

作为一般规则,如果您只需要在开头填充一次数据,请使用componentDidMount

如果必须收集更多数据(基于道具更改),请使用componentWillReceiveProps

作为旁注,对于您的特定示例,您似乎通过循环多次调用后端。从问题中无法分辨,但如果您使用的是诺言库,您可以将所有这些承诺推送到数组中,然后执行Promise.all().then(/* update state */)

答案 1 :(得分:0)

首先,要将其置于一个状态而不使其成为无限循环,您需要一个构造函数方法,您可以在其中调用super()方法,然后您就可以定义状态。所以你需要这样的东西:

constructor() {
    super();
    this.state = {
       allroles: null
    }
}

之后,正如您正确指出的那样,您需要生命周期钩子:componentDidMount()

您需要第一个填充您的州。所以要调用它,应该看起来像这样:

componentDidMount() {
   this.setState({allroles: this.getRoles()})
}

现在,如果您需要获取新更新,您必须知道此更新是否将作为用户的交互结果(例如,他们在表上添加新条目)或基于时间的更新(例如,使用调用{的间隔)来触发每隔一定时间{1}}。无论哪种方式,您都需要再次使用this.getRole()

在这种情况下,您不需要this.setState({allroles: this.getRoles()}),因为此组件在不接收道具的情况下管理自己的状态。如果您已经收到角色作为道具,则需要componentWillReceiveProps()

最后,回答为什么你有一个无限循环是因为componentWillReceiveProps()触发了组件的重新渲染。这意味着,如果您在this.setState()方法中致电this.setState(),则

  1. 渲染()
  2. 重新呈现触发
  3. 渲染()
  4. 重新渲染触发 ...
  5. 这就是为什么你不能在你的render()方法中直接调用this.setState()而是在事件或某些生命周期钩子上调用它(在渲染发生之前被调用的那些或被认为是成为INITIAL渲染)