Реагировать на проблему с маршрутизатором при получении данных - PullRequest
0 голосов
/ 30 сентября 2018

У меня сложная ситуация с реагирующим маршрутизатором 4. Представьте, что у меня есть маршрут

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

Теперь вы можете видеть, что я использую render в Route, что означает, что он будет не размонтируйте этот компонент при каждом рендеринге, скорее обновите старый экземпляр новыми реквизитами.

Однако в некоторый момент внутри ProductList пользователь вызывает функцию setFlag, которую вы можете видеть, обновляет некоторое свойство вparent.

Из-за этого вызывается повторная передача родителя.Который также называет componentWillReceiveProps(CWRP) из ProductList.Внутри CWRP из ProductList Я всегда (безоговорочно) выбираю предметы с новыми реквизитами.

Это вызывает мою проблему. Вы можете видеть, что когда пользователь обновил флаг, не было необходимости извлекать данные снова в CWRP, потому что обновление этого флага не было связано с моими данными.

Вы могли бы сказать, что я долженПоместите в CWRP некоторое условие, которое будет выполнять некоторые проверки и извлекать данные только тогда, когда это необходимо.Однако я считаю невозможным подобную проверку.Потому что, например, ProductList получает поисковый запрос.Я мог бы, например, сравнить поисковый термин из предыдущего рендера с поисковым термином нового рендера, и если они отличаются, то для выборки данных, однако, это неверно, потому что даже в случае одного и того же поискового термина выборка должна быть выдана (возможно, данные были обновлены на сервере).

Какое решение вы видите в такой ситуации?Чтобы мой список продуктов не получал данные каждый раз, когда флаг родителя меняется?

1 Ответ

0 голосов
/ 30 сентября 2018

Поднимите свое состояние и переместите свою логику из метода render в родительский container-component, затем используйте this.setState(), чтобы остановить обновления состояния ИЛИ используйте 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 контролирует несколько component детей.Дети могут обновить родителя с помощью переданного родительского метода.

В этом простом примере searchForPlayer onChange и onSubmit обновляет состояние searchTerm родителя и изменяет запрос URL-адреса с помощью метода handleSubmit родителя.Изменение URL-запроса вызывает метод родительского элемента componentDidUpdate, который затем выбирает новые данные и обновляет компонент displayPlayerList.

URL-адрес до:

/players/all

URL-адрес после отправки формы:

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

Так что, если пользователь вводит данныеURL-адрес:

/players/player?number=10

или

/players/fdskmsdfk?number=10

и нажмите Enter, он загрузит отфильтрованный список, потому что он только ищетдля запроса number.

Если они перейдут на:

/player/dsfdsdfdsdf

или

player/1223345

или что-либо без запроса numberвместо этого он просто выберет всех игроков ( это можно обрабатывать по-разному, но для простоты ).

Рабочий пример: https://codesandbox.io/s/xn3p3o6vq

Containers / PlayersList.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 / searchForPlayer.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>
);

компоненты / displayPlayerList.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>
);

компоненты / noPlayerFound.js (дочерний компонент)

import React from "react";

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

component / serverError.js (дочерний компонент)

import React from "react";

export default ({ err }) => (
  <div style={{ color: "red", padding: 20 }}>
    <i style={{ marginRight: 5 }} className="fas fa-exclamation-circle" /> {err}
  </div>
);
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...