我有两个用于获取和渲染数据的组件。我想要做的是在点击时更改CarSearch
组件中该按钮的标签(理想情况下也是CSS),当搜索完成时,我想在那里放置基本的“搜索”标签。
但是,当我单击搜索按钮并完成搜索时,标签“搜索”未设置,并且停留在那里的标签仍然是“过滤...”。现在改变了什么?
也许我只是过于复杂,并且有更好的方法来实现它。
class Car extends Component {
constructor() {
super();
this.state = {
cars: [],
searchBtn: 'Searchh'
}
}
componentDidMount() {
axios.get('/api/cars')
.then((response) => {
this.setState({cars: response.data});
console.log('cars: ', cars);
}).catch(err => {
console.log('CAUGHT IT! -> ', err);
});
}
handleSearch = () => {
axios.post('/api/cars/search', searchCars)
.then(response => {
this.setState({cars: response.data, searchBtn: 'Seach'}) // however, this `searchBtn` will not be passed to `CarSeach`
console.log('response.data: ', response.data);
})
}
render() {
return (
...
<CarAddNew />
<CarSearch
onSearch={this.handleSearch}
searchBtnLabel={this.state.searchBtn}
/>
<CarList cars={this.state.cars} />
)
}
}
export default class CarSearch extends Component {
constructor(props){
super(props);
searchBtn: props.searchBtnLabel
}
handleSearchSubmit(e) {
e.preventDefault();
this.setState({ searchBtn: 'Filtering...'});
this.props.onSearch(...)
}
render() {
return(
... search form ...
<button type="submit" className="btn btn-primary btn-sm mx-sm-2">
{this.state.searchBtn}
</button>
)
}
正如你所写,有两个组件(Car
和CarSearch
)试图控制标签。使用React的最佳方法是拥有“黄金”的真相来源。
有两种方法可以实现它:
让你的CarSearch
渲染从Car
传递给它的标签。您甚至可以使CarSearch组件无状态。
export default class CarSearch extends Component {
handleSearchSubmit(e) {
e.preventDefault();
this.props.onSearch(...)
}
render() {
return(
... search form ...
<button type="submit" className="btn btn-primary btn-sm mx-sm-2">
{this.props.searchBtnLabel}
</button>
)
}
}
更改onSearch
组件的Car
函数以明确管理搜索状态。
handleSearch = () => {
this.setState({searchBtn: 'Filtering...'});
axios.post('/api/cars/search', searchCars)
.then(response => {
this.setState({cars: response.data, searchBtn: 'Search'})
console.log('response.data: ', response.data);
})
}
CarSearch
组件处理与搜索相关的所有内容。export default class CarSearch extends Component {
handleSearchSubmit(e) {
e.preventDefault();
this.setState({searchBtn: 'Filtering...'});
axios.post('/api/cars/search', searchCars)
.then(response => {
this.setState({searchBtn: 'Search'});
this.props.onSearch(response.data);
})
}
}
render() {
return(
... search form ...
<button type="submit" className="btn btn-primary btn-sm mx-sm-2">
{this.state.searchBtn}
</button>
)
}
}
然后在您的父组件中:
handleSearch = (data) => {
this.setState({cars: data})
}
哪一个选择?
这取决于组件职责是什么。对于e.x.在搜索时,如果你想在Car
中的其他组件上反映出来,那么e.x.在搜索时禁用AddCar,然后Parent应该负责。如果不是,搜索组件可以拥有搜索状态。
另一个提示,我建议保持搜索组件的状态,而不是依赖标签显示。对于e.x.您的搜索可能有许多状态:初始|搜索|搜索失败|搜索成功与0记录|使用超过0条记录成功搜索。
使搜索状态显式化,允许您以更加可预测的模式控制渲染。
class CarSearch extends Component {
state: {
searchState: 'Initial',
searchTerm: ''
}
onSearch () => {
this.setState({searchState: 'Searching'})
axios.post(---)
.then(this.setState({searchState: 'Success'}))
.catch(this.setState({searchState: 'Error'}))
}
render() {
// change styles, conditional rendering, show toast etc. based on the state
}
}
在这里,constructor
组件的CarSearch
方法在安装后只会被调用一次。永远不会再被召唤。所以,你可以做的是在componentDidUpdate
组件中使用CarSearch
方法,并且当传入的searchBtnLabel
prop与你现在的状态不同时使用setState。
在CarSearch
构造函数调用之后输入以下代码。
componentDidUpdate() {
if (this.props.searchBtnLabel !== this.state.searchBtn) {
this.setState({ searchBtn: this.props.searchBtnLabel });
}
}
这将解决您的问题。
您在评论中提到过滤阶段没有发生。你可以解决它,像
handleSearchSubmit(e) {
e.preventDefault();
this.setState({ searchBtn: 'Filtering...'}, () => {
this.props.onSearch(...); // setTimeout(() => this.props.onSearch(...), N) where N is milliseconds.
});
}
说明:setState
可以接受一个回调函数,该函数将在状态设置成功后执行。即使在使用回调之后,如果您没有看到过滤文本,也可以使用我提到的setTimeout
作为注释。