【问题标题】:React Router Link is changing URL but the component remains sameReact Router Link 正在更改 URL,但组件保持不变
【发布时间】:2021-08-30 14:17:48
【问题描述】:

所以我遇到了一个问题,当单击链接时,URL 会发生变化,但视图保持不变,直到我刷新页面。

我研究了许多解决方案,唯一有效的方法是强制重新加载我不想要的页面,因为 React 是一个 SPA(单页应用程序)。我已经尝试了 history.push() 和 Link 两者,但输出保持不变。如果您需要查看其他文件,这是我的repo

App.js

import React from 'react';
import { BrowserRouter as Router, Switch, Route } from 'react-router-dom';

import Header from './components/header/header.component';

import HomePage from './pages/homepage/homepage.component';
import Details  from './pages/detail/detail.component';

import './App.scss';

const App = () => (
    <Router>
        <Header />
        <Switch>
            <Route exact path="/" component={HomePage} />
            <Route path="/:name" component={Details}/>
        </Switch>
    </Router>
);

细节组件

import React from 'react';

import LinkButton from '../../components/link-button/link-button.component';

import './detail.style.scss';

class Details extends React.Component {
    constructor(props) {
        super(props);
        this.state = {
            borders: [],
            country: '',
        };
    }

    async componentDidMount() {
        const { name } = this.props.match.params;
        fetch(`https://restcountries.eu/rest/v2/name/${name}?fullText=true`)
            .then((res) => res.json())
            .then((data) => {
                this.setState({ country: data[0] });
                return data[0].borders;
            })
            .then((country) => {
                for (let i = 0; i < country.length; i++) {
                    if (i > 2) break;
                    fetch(`https://restcountries.eu/rest/v2/alpha/${country[i]}`)
                        .then((res) => res.json())
                        .then((data) =>
                            this.setState({ borders: [...this.state.borders, data.name] })
                        );
                }
            });
    }

    render() {
        const { country, borders } = this.state;
        if (country !== '') {
            return (
                <div className="detail-container">
                    <div className="detail-back-btn">
                        <LinkButton value="/">
                            <i className="fas fa-long-arrow-alt-left icon"></i> Back
                        </LinkButton>
                    </div>
                    <div className="detail-stats">
                        <img className="detail-flag" alt="Flag" src={country.flag} />
                        <div className="detail-text-container">
                            <h1 className="heading">{country.name}</h1>
                            <div className="detail-text">
                                <div className="left">
                                    <p className="text">
                                        Native Name: <span>{country.nativeName}</span>
                                    </p>
                                    <p className="text">
                                        Population:
                                        <span>{country.population.toLocaleString()}</span>
                                    </p>
                                    <p className="text">
                                        Region: <span>{country.region}</span>
                                    </p>
                                    <p className="text">
                                        Sub Region: <span>{country.subregion}</span>
                                    </p>
                                    <p className="text">
                                        Capital: <span>{country.capital}</span>
                                    </p>
                                </div>
                                <div className="right">
                                    <p className="text">
                                        Top Level Domain: <span>{country.topLevelDomain}</span>
                                    </p>
                                    <p className="text">
                                        Currencies:{' '}
                                        <span>
                                            {country.currencies.map((e) => e.name).join(', ')}
                                        </span>
                                    </p>
                                    <p className="text">
                                        Languages:{' '}
                                        <span>
                                            {country.languages.map((e) => e.name).join(', ')}
                                        </span>
                                    </p>
                                </div>
                            </div>

                            <div className="border">
                                <p className="border-text">Border Countries:</p>
                                <span className="border-btn">
                                    {borders.map((border, index) => (
                                        <LinkButton key={index} value={border}>
                                            {border}
                                        </LinkButton>
                                    ))}
                                </span>
                            </div>
                        </div>
                    </div>
                </div>
            );
        } else return null;
    }
}

export default Details;

链接按钮组件

import React from 'react';
import { Link, withRouter } from 'react-router-dom';

import './link-button.style.scss';

//this is the brute force reload solution
//which is working, but i need a better approach
function refreshPage() {
    setTimeout(() => {
        window.location.reload(false);
    }, 0);
    console.log('page to reload');
}

const LinkButton = ({ value, children, history, match, location }) => {
    console.log(history, match, location);
    return (
        <Link to={value} onClick={refreshPage}>
            <button
                className="link-btn"
                value={value}
            >
                {children}
            </button>
        </Link>
    );
};

export default withRouter(LinkButton);

【问题讨论】:

    标签: javascript reactjs react-router react-router-dom


    【解决方案1】:

    您看到的是从一个 URL 到另一个 URL 的变化,但它们都匹配相同的 Route 路径,因此 react-router 不会重新安装组件。这是故意的,如果您考虑 Switch 组件的用途,这也是有道理的。

    例如:“/a”和“/b”都匹配&lt;Route path="/:name" component={Details}/&gt;。因此,当从一个更改为另一个时,react-router 没有理由重新安装 Details 组件,因为它仍然匹配。

    要完成您尝试做的事情,您需要监听路由参数(name 属性)中的更新。

    对此的一种策略是使用componentDidUpdate生命周期方法来检查值是否已更改:

    componentDidUpdate(prevProps) {
      if (this.props.match.params.name !== prevProps.match.params.name) {
        // Refetch your data here because the "name" has changed.
      }
    }
    

    请注意,componentDidUpdate 不会在初始渲染时调用,因此您将需要这两种生命周期方法。但是,您可以将您的 fetch 调用提取到它自己的方法中,以便您的 componentDidMountcomponentDidUpdate 可以重用相同的代码。

    对于那些使用带有钩子的功能组件的人来说,这个监听过程会变得更容易一些。使用带有路由参数的useEffect 钩子作为依赖项将完成与类版本中的两个生命周期方法相同的事情。

    const { name } = useParams();
    
    useEffect(() => {
      // Fetch data
    }, [name]);
    

    【讨论】:

    • 非常感谢您提供详细的解决方案
    【解决方案2】:

    我真的不知道这是否是不好的做法,但我遇到了同样的问题,到目前为止我的临时解决方案是通过链接标签上的目标“_top”属性强制窗口重新加载。

    <Nav>
      <Link to="/" target="_top">
        <FaHome size={24} />
      </Link>
      <Link to="/user" target="_top">
        <FaSignInAlt size={24} />
      </Link>
      <Link to="/login" target="_top">
        <FaUserAlt size={24} />
      </Link>
    </Nav>

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2021-08-31
      • 2019-07-01
      • 1970-01-01
      • 2018-05-25
      • 2019-11-23
      • 2021-10-21
      • 1970-01-01
      相关资源
      最近更新 更多