反应路由器问题在需要时获取数据

时间:2018-09-30 12:18:38

标签: javascript reactjs react-router

我对React Router 4有一个棘手的情况。 想象我有一条路线

<Route path='/search/:term?' render={(props) => {
                return (<ProductList
                   setFlag={(flag)=>this.setState({flag})}
                   {...props}
                   />)
              }} />

现在您可以看到我在Route中使用了 render ,这意味着它将在每个渲染中不卸载该组件,而是使用新的道具更新旧实例。

但是,在ProductList内部的某个时刻,用户调用了setFlag函数,您可以看到该函数更新了parent中的某些属性。

因此,导致了父级的重新提交。也称为componentWillReceiveProps(CWRP)中的ProductList。在CWRP的{​​{1}}内部,我总是(无条件)用新道具来取物品。

这导致了我的问题。 您可以看到,当用户更新标志时,无需在CWRP中再次获取数据,因为更新该标志与我的数据无关。

您可以说我应该在CWRP中设置一些条件,该条件仅在必要时才进行检查和获取数据。但是,我发现不可能提出这样的检查。例如,因为ProductList接收搜索词。例如,我可以将先前渲染的搜索词与新渲染的搜索词进行比较,如果它们不同,则将其提取以获取数据,但这是不正确的,因为即使在搜索词相同的情况下,也应发出提取指令(也许数据是在服务器上更新的。)

在这种情况下,您看到什么解决方案? 这样我的产品列表就不会在每次父级标志更改时都获取数据吗?

1 个答案:

答案 0 :(得分:0)

提升您的状态并将您的逻辑从render方法中移到父container-component中,然后利用this.setState()停止状态更新 OR 使用{ {1}}继续允许状态更新,但在shouldComponentUpdate()尚未更改的情况下停止重新渲染(任何一个都会阻止flag的更新):

ProductList

然后路线将更改为:

import React, { Component } from 'react';
import ProductList from './ProductList';

export default class SearchTerms extends Component {
  state = { flag: '' };

  shouldComponentUpdate = (nextProps, nextState) => ( this.state.flag !== nextState.flag )

  handleFlag = flag => this.setState(prevState => { return this.state.flag !== flag ? { flag } : null })    

  render = () => ( <ProductList setFlag={this.handleFlag} {...this.state} {...this.props} /> )

}

此外,我将避免完全使用 <Route path='/search/:term?' component={SearchTerms} /> ,而应使用componentWillReceiveProps()


下面是一个示例,其中父componentDidUpdate()控制着几个container-component子。子代可以通过向下传递的父代方法更新父代。

在这个简单的示例中,searchForPlayer的componentonChange更新父级的onSubmit状态,并通过父级的searchTerm方法更改URL查询。 URL查询更改触发了父级的handleSubmit方法,该方法随后获取新数据并更新componentDidUpdate组件。

之前的URL

displayPlayerList

提交表单后的网址:

/players/all

因此,如果用户将URL输入到:

/players/player?number=${this.state.searchTerm}

/players/player?number=10

,然后按回车,它将加载已过滤列表,因为它仅在寻找/players/fdskmsdfk?number=10查询。

如果他们去:

number

/player/dsfdsdfdsdf

或没有player/1223345查询的任何内容,那么它将仅获取所有玩家(此处理方式可能有所不同,但为简单起见)。

工作示例:https://codesandbox.io/s/xn3p3o6vq

containers / PlayersList.js (父容器组件)

number

components / searchForPlayer.js (子组件)

import isEmpty from "lodash/isEmpty";
import React, { Component, Fragment } from "react";
import qs from "qs";
import DisplayPlayerList from "../components/displayPlayerList";
import NoPlayerFound from "../components/noPlayerFound";
import SearchForPlayer from "../components/searchForPlayer";
import ServerError from "../components/serverError";
import Spinner from "../components/spinner";

export default class PlayersList extends Component {
  state = {
    err: "",
    isLoading: true,
    searchTerm: "",
    players: [],
    noplayer: "",
    number: ""
  };

  componentDidMount = () => this.fetchPlayers();

  componentDidUpdate = (prevProps, prevState) => this.props.location.search !== prevProps.location.search && this.fetchPlayers();

  fetchPlayers = () => {
    const { number } = qs.parse(this.props.location.search, { ignoreQueryPrefix: true })

    fetch(`https://jsonplaceholder.typicode.com/users${number ? `/${number}` : ""}`)
      .then(response => response.json())
      .then(players =>
        this.setState({
          err: "",
          players: !number ? [...players] : [players],
          noplayer: isEmpty(players) ? true : false,
          isLoading: false,
          number,
          searchTerm: ""
        })
      )
      .catch(err => this.setState({ err: err.toString() }));
  };

  handleChange = e => this.setState({ searchTerm: e.target.value });

  handleSubmit = e => {
    e.preventDefault();
    this.props.history.push(`/players/player?number=${this.state.searchTerm}`);
  };

  render = () => (
    this.state.isLoading // (if isLoading is true..)
      ? <Spinner />  // (then show a spinner)
      : <div style={{ padding: 20, width: 500 }}> // (otherwise...)
          <SearchForPlayer  // (show player search form and...)
            handleChange={this.handleChange}
            handleSubmit={this.handleSubmit}
            {...this.state}
          />
          { this.state.err // (if there's an error...)
            ? <ServerError {...this.state} /> // (show the error)
            : this.state.noplayer // (otherwise, if there's no player...)
              ? <NoPlayerFound {...this.state} /> // (show no player found)
              : <DisplayPlayerList {...this.state} /> // (otherwise, display updated list)
          }
        </div>
  );
}

components / displayPlayerList.js (子组件)

import React from "react";

export default ({ handleChange, handleSubmit, searchTerm }) => (
  <form onSubmit={handleSubmit}>
    <input
      className="uk-input"
      type="number"
      value={searchTerm}
      onChange={handleChange}
      placeholder="Search for player by number..."
      style={{ width: 300, marginRight: 10 }}
      min={1}
    />
    <button
      disabled={!searchTerm}
      className="uk-button uk-button-primary"
      type="submit"
    >
      Search
    </button>
  </form>
);

components / noPlayerFound.js (子组件)

import map from "lodash/map";
import React from "react";

export default ({ players }) => (
  <ul style={{ listStyleType: "none" }}>
    {map(players, ({ id, name, username, email }) => (
      <li style={{ margin: "10px 0" }} key={id}>
        <strong>Player # {id}</strong>
        <span> - {name}</span>
      </li>
    ))}
  </ul>
);

component / serverError.js (子组件)

import React from "react";

export default ({ number }) => (
  <div style={{ color: "red", padding: 20 }}>
    No player was found matching #{number}!
  </div>
);