React应用提交数据后无法无刷新展示,求助原因及解决方法
问题分析与解决方案
嘿,这个问题我太熟悉了!核心原因是两个组件的状态完全隔离,导致新增画廊后页面没有同步更新,咱们来一步步理清楚:
问题根源
你的Home组件和AddCategory组件各自维护了独立的galleries状态:
Home组件在componentDidMount时从API获取画廊列表,存在自己的state里,用来渲染页面上的画廊卡片AddCategory组件在POST成功后,只更新了自己的galleriesstate,但这个state和Home里的完全没关系,所以Home的画廊列表还是旧数据,只有刷新页面重新GET才会拿到新内容
解决方案:统一状态管理或触发父组件刷新
下面给两种最常用的解决思路,你可以根据项目规模选择:
方案1:状态提升(推荐小项目使用)
把画廊列表的状态统一放在Home组件中,AddCategory只负责发起请求,然后通知Home更新状态:
步骤1:修改Home组件
- 把获取画廊的逻辑抽成独立方法,方便复用
- 新增一个方法,用来接收新的画廊数据并更新state
- 将更新方法传递给
AddCategory组件作为props
class Home extends Component { constructor(props) { super(props); this.state = { galleries: [], isLoading: false, error: null, }; this.fetchGalleries = this.fetchGalleries.bind(this); this.handleGalleryUpdate = this.handleGalleryUpdate.bind(this); } // 抽离获取画廊的方法 fetchGalleries() { this.setState({ isLoading: true }); fetch('http://.../gallery') .then((response) => response.json()) .then((data)=>this.setState({galleries: data.galleries, isLoading: false})) .catch(error => this.setState({ error, isLoading: false})); } componentDidMount() { this.fetchGalleries(); } // 接收新的画廊数据,更新Home的state handleGalleryUpdate(newGalleries) { this.setState({ galleries: newGalleries }); } render() { const {galleries, isLoading, error} = this.state; if (error) { return <p>{error.message}</p>; } if (isLoading) { return <div className="loader-wrapper"><div className="loader"/></div>; } return ( <div className="categories"> { galleries.length > 0 ? galleries.map((gallery) => { return ( <Card key={gallery.path}> ... </Card> )}) : null } {/* 把更新方法传给AddCategory */} <AddCategory onGalleryUpdate={this.handleGalleryUpdate} /> </div> ); } }
步骤2:修改AddCategory组件
- 删除自己的
galleriesstate,不需要维护独立的列表数据 - POST成功后,调用props传过来的
onGalleryUpdate方法,把最新的画廊列表传给Home
class AddCategory extends Component { constructor(props) { super(props); this.state = { modal: false, isLoading: false, error: null, }; this.toggle = this.toggle.bind(this); this.handleClick = this.handleClick.bind(this); } toggle() { this.setState({ modal: !this.state.modal }); } handleClick(event) { event.preventDefault(); this.setState({ modal: !this.state.modal, isLoading: true }); fetch('http://.../gallery', { method: 'POST', headers: {'Content-Type':'application/json'}, body: JSON.stringify({"name": this.galleryName.value}) }) .then((response) => { if (response.ok) { return response.json(); } else { throw new Error('Something went wrong ...') } }) .then((data)=>{ // 调用Home传过来的方法,更新父组件的状态 this.props.onGalleryUpdate(data.galleries); this.setState({ isLoading: false }); }) .catch(error => this.setState({ error, isLoading: false})); } render() { const {modal, isLoading, error} = this.state; if (error) { return <p>{error.message}</p>; } if (isLoading) { return <div className="loader-wrapper"><div className="loader"/></div>; } return ( <Card className="add"> <div className="link" onClick={this.toggle}> <CardBody> <CardTitle>Add gallery</CardTitle> </CardBody> </div> <Modal isOpen={modal} toggle={this.toggle} className={this.props.className}> <div className="modal-header"> ... </div> <ModalBody> <form className="form-inline addCategoryForm"> <div className="group"> <input type="text" ref={(ref) => {this.galleryName = ref}} id="inputGalleryName" name="galleryName" required/> <label>name of the gallery</label> </div> <Button onClick={this.handleClick} color="success">Add</Button> </form> </ModalBody> </Modal> </Card> ); } }
方案2:触发父组件重新获取数据
如果你的API返回的不是完整的画廊列表,而是单个新增的画廊,或者你更倾向于重新拉取最新数据,可以让AddCategory在POST成功后通知Home重新调用GET接口:
修改Home组件(和方案1类似,抽离fetchGalleries方法)
// 保留fetchGalleries方法,然后传给AddCategory <AddCategory onGalleryAdded={this.fetchGalleries} />
修改AddCategory组件的handleClick方法
.then((data)=>{ // 通知Home重新获取画廊列表 this.props.onGalleryAdded(); this.setState({ isLoading: false }); })
这样Home会重新发起GET请求,拿到最新的画廊列表并更新state,页面就会自动刷新了。
额外小提示
- 尽量避免在多个组件中维护相同的状态,很容易出现不同步的问题
- 如果项目后续规模变大,可以考虑使用React Context或者Redux来统一管理全局状态,这样状态同步会更方便
内容的提问来源于stack exchange,提问作者Zoltan Kovacs
相关产品推荐
相关产品推荐

