React Router Link点击URL变更但组件不更新,求非强制刷新解决方案
问题根源
该现象是React Router组件复用机制导致的:当你从一个/:name路由跳转到另一个/:name路由时,两个路由匹配的都是同一个Details组件,React不会销毁旧组件再重新挂载,只会更新组件的props,所以仅在挂载时执行的componentDidMount生命周期不会再次触发,自然不会拉取新的国家数据,视图也就不会更新。
第一步:移除LinkButton的强制刷新逻辑
直接删掉LinkButton里的刷新相关代码,不需要暴力重载页面:
import React from 'react'; import { Link, withRouter } from 'react-router-dom'; import './link-button.style.scss'; const LinkButton = ({ value, children }) => { return ( <Link to={value}> <button className="link-btn" value={value} > {children} </button> </Link> ); }; export default withRouter(LinkButton);
第二步:给Details组件添加参数更新监听
在Details类组件里新增componentDidUpdate生命周期,判断路由参数变化后重新拉取数据即可,同时优化了请求逻辑避免竞态和多余重渲染:
class Details extends React.Component { constructor(props) { super(props); this.state = { borders: [], country: '', }; // 用于取消上一次未完成的请求,避免竞态 this.controller = null; } fetchCountryData(name) { // 先取消之前的未完成请求 if (this.controller) this.controller.abort(); this.controller = new AbortController(); const signal = this.controller.signal; // 先重置状态,避免旧数据残留 this.setState({ country: '', borders: [] }); fetch(`https://restcountries.eu/rest/v2/name/${name}?fullText=true`, { signal }) .then((res) => res.json()) .then((data) => { this.setState({ country: data[0] }); return data[0].borders; }) .then((borders) => { const borderPromises = borders.slice(0,3).map(code => fetch(`https://restcountries.eu/rest/v2/alpha/${code}`, { signal }).then(res => res.json()) ); return Promise.all(borderPromises); }) .then(borderData => { this.setState({ borders: borderData.map(item => item.name) }); }) .catch(err => { if (err.name !== 'AbortError') console.error('请求失败:', err); }); } componentDidMount() { const { name } = this.props.match.params; this.fetchCountryData(name); } componentDidUpdate(prevProps) { // 只有路由参数name变化时才重新拉取数据 const prevName = prevProps.match.params.name; const currentName = this.props.match.params.name; if (prevName !== currentName) { this.fetchCountryData(currentName); } } componentWillUnmount() { // 组件卸载时取消未完成的请求 if (this.controller) this.controller.abort(); } // 保留原有render逻辑即可 }
可选优化:改用函数组件写法
后续重构可以使用函数组件+Hook的写法,逻辑会更简洁:
import React, { useState, useEffect } from 'react'; // 其他导入保持不变 const Details = (props) => { const [country, setCountry] = useState(''); const [borders, setBorders] = useState([]); const name = props.match.params.name; useEffect(() => { const controller = new AbortController(); const signal = controller.signal; setCountry(''); setBorders([]); fetch(`https://restcountries.eu/rest/v2/name/${name}?fullText=true`, { signal }) .then(res => res.json()) .then(data => { setCountry(data[0]); return data[0].borders.slice(0,3); }) .then(borderCodes => Promise.all( borderCodes.map(code => fetch(`https://restcountries.eu/rest/v2/alpha/${code}`, { signal }).then(res => res.json()) ) )) .then(borderData => setBorders(borderData.map(item => item.name))) .catch(err => { if (err.name !== 'AbortError') console.error(err); }); return () => controller.abort(); }, [name]); // 依赖项为name,name变化时自动重新执行请求逻辑 // 保留原有render逻辑即可 } export default Details;
内容的提问来源于stack exchange,提问作者Aman Gupta
相关产品推荐
相关产品推荐

