何时使用 Flux 加载异步关系

When to load an async relation with Flux

假设您有一个显示宠物资源的宠物组件。您还通过 ownerId 属性拥有属于 pet 的所有者资源。您应该在 Pet 组件的哪个位置加载所有者资源?

const Pet = React.createClass({
    getInitialState() {
        return {
            pet: PetStore.get(this.props.petId)
        };
    },

    componentDidMount() {
        PetStore.addChangeListener(this._onChange);
    },

    componentWillUnmount() {
        PetStore.removeChangeListener(this._onChange);
    },

    render() {
        var owner; // where should I initiate the ajax request to get owner? 

        return (
            <p>{owner.name}</p>
        );
    },

    onChange() {
        this.setState({
            pet: PetStore.get(this.props.petId)
        });
    }
});

React documentation recommendscomponentDidMount 中执行此操作。

正如 Thilo 提到的,React 文档建议您在 componentDidMount 中异步加载数据。在 componentDidMount 而不是 componentWillMount 上执行此操作的原因是,如果您 运行 在服务器上对初始页面加载做出反应,那么 componentWillMount 将被执行,而您没有想在服务器上进行 Ajax 调用。

话虽如此,我建议您将数据获取逻辑移至另一个组件(在 React 世界中通常称为视图控制器)。这意味着您有两个组件,而不是让一个组件同时执行数据获取和渲染。一个负责获取数据,另一个负责呈现该数据。您将该数据作为道具从视图控制器组件传递给渲染组件。一个小例子是:

var Pet = React.createClass({
  render() {
    return (
      <p>Pet: {this.props.pet.name}</p>
      <p>Owner: {this.props.owner.name}</p>
    );
  }
});

var PetController = React.createClass({
  getInitialState() {
    return {
      pet: PetStore.get(this.props.petId),
      owner: null
    };
  },
  componentDidMount() {
    OwnerStore.getOwner(this.state.pet.ownerId)
      .then(owner => this.setState({owner: owner}));
  },
  render() {
    return <Pet pet={this.state.pet} owner={this.state.owner} />;
  }
});

这种分离为您提供的是更简单、更集中的组件。 Pet 组件不必担心从哪里获取数据,它可以在您从 PetStore 以外的其他地方获取数据的其他情况下重复使用。而 PetController 组件只专注于数据获取和状态管理。为了使 Pet 组件更简单,您可以在获取所有者之前避免渲染它。类似于:

var PetController = React.createClass({
  getInitialState() {
    return {
      pet: PetStore.get(this.props.petId),
      owner: null
    };
  },
  componentDidMount() {
    OwnerStore.getOwner(this.state.pet.ownerId)
      .then(owner => this.setState({owner: owner}));
  },
  render() {
    if (this.state.pet && this.state.owner) {
      return <Pet pet={this.state.pet} owner={this.state.owner} />;
    } else {
      return <div className="spinner" />;
    }
  }
});

那么您的 Pet 组件就不必担心异步数据获取,因为在数据存在之前它不会被渲染。