我对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接收搜索词。例如,我可以将先前渲染的搜索词与新渲染的搜索词进行比较,如果它们不同,则将其提取以获取数据,但这是不正确的,因为即使在搜索词相同的情况下,也应发出提取指令(也许数据是在服务器上更新的。)
在这种情况下,您看到什么解决方案? 这样我的产品列表就不会在每次父级标志更改时都获取数据吗?
答案 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的component
和onChange
更新父级的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>
);