Поднимите свое состояние и переместите свою логику из метода 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>
);