反应路由器链接不会导致组件在嵌套路由内更新 [英] React router Link not causing component to update within nested routes

查看:65
本文介绍了反应路由器链接不会导致组件在嵌套路由内更新的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

这让我发疯.当我尝试在嵌套路由中使用 React Router 的链接时,浏览器中的链接会更新,但视图没有改变.但是,如果我将页面刷新到链接,它确实如此.不知何故,组件没有在它应该更新的时候更新(或者至少这是目标).

这是我的链接的样子(上一个/下一个项目实际上是变量):

<button className="button button-xs">上一个</button></链接><链接到={'/portfolio/next-item'}><button className="button button-xs">下一步</button></链接>

一个笨拙的解决方案是手动调用 forceUpate() 如下:

这行得通,但会导致整页刷新,这是我不想要的和错误:

ReactComponent.js:85 未捕获的类型错误:无法读取未定义的属性enqueueForceUpdate"

我四处寻找答案,最接近的是:https://github.com/reactjs/react-router/issues/880.但它很旧,我没有使用纯渲染混合.

以下是我的相关路线:

<Route path='/' component={Home}><Route path="/index:hashRoute" component={Home}/></路线><Route path="/portfolio" component={PortfolioDetail} ><Route path="/portfolio/:slug" component={PortfolioItemDetail}/></路线><Route path="*" component={NoMatch}/></路线>

无论出于何种原因,调用 Link 都不会导致组件重新安装,这需要发生以获取新视图的内容.它确实调用了 componentDidUpdate,我确定我可以检查 url slug 更改,然后在那里触发我的 ajax 调用/视图更新,但似乎不需要这样做.

编辑(更多相关代码):

PortfolioDetail.js

import React, {Component} from 'react';从反应路由器"导入 { browserHistory }从'react-redux'导入{connect};从../components/common/loader"导入加载器;从'../components/portfolio-detail/portfolioItemDetail'导入PortfolioItemDetail;从../actions/portfolio"导入 * 作为组合动作;导出默认类 PortfolioDetail 扩展组件 {静态 readyOnActions(调度,参数){//在服务器上渲染时会触发此操作,然后再次使用每个 componentDidMount.//但不使用链接触发...返回 Promise.all([调度(portfolioActions.fetchPortfolioDetailIfNeeded(params.slug))]);}componentDidMount() {//react-router Link 不会触发此事件const {dispatch, params} = this.props;PortfolioDetail.readyOnActions(dispatch, params);}componentWillUnmount() {//react-router Link 不会触发此事件this.props.dispatch(portfolioActions.resetPortfolioDetail());}renderPortfolioItemDetail(browserHistory) {const {DetailReadyState, item} = this.props.portfolio;如果(DetailReadyState === 'WORK_DETAIL_FETCHING'){返回<加载器/>;} else if (DetailReadyState === 'WORK_DETAIL_FETCHED') {return ;//当路由被嵌套时,曾经将 this 作为 this.props.children} else if (DetailReadyState === 'WORK_DETAIL_FETCH_FAILED') {browserHistory.push('/not-found');}}使成为() {返回 (<div id="内部页面">{this.renderPortfolioItemDetail(browserHistory)}

);}}函数 mapStateToProps(state) {返回 {投资组合:state.portfolio};}函数 mapDispatchToProps(dispatch) {返回 {派遣:派遣}}导出默认连接(mapStateToProps, mapDispatchToProps)(PortfolioDetail);

PortfolioItemDetail.js

import React, {Component} from 'react';从'react-redux'导入{connect};从 './gallery' 导入图库;导出默认类 PortfolioItemDetail 扩展 React.Component {制作画廊(画廊){如果(画廊){返回画廊.split('|').map((image, i) => {return <li key={i}><img src={'/images/portfolio/' + image} alt=""/></li>})}}使成为() {const { item } = this.props.portfolio;返回 (<div className="portfolio-detail container-fluid"><画廊makeGallery={this.makeGallery.bind(this)}项目={项目}/>

);}}函数 mapStateToProps(state) {返回 {投资组合:state.portfolio};}导出默认连接(mapStateToProps)(PortfolioItemDetail);

gallery.js

import React, { Component } from 'react';从反应路由器"导入{链接};const Gallery = (props) =>{const {gallery, prev, next} = props.item;const prevButton = prev ?<Link to={'/portfolio/' + prev}><button className="button button-xs">Previous</button></Link>:'';const nextButton = 下一个?<链接到={'/portfolio/' + next}><button className="button button-xs">Next</button></Link>:'';返回 (<div><ul className="画廊">{props.makeGallery(画廊)}<div className="next-prev-btns">{上一个按钮}{下一个按钮}

);};导出默认图库;

新路线,基于 Anoop 的建议:

<Route path='/' component={Home}><Route path="/index:hashRoute" component={Home}/></路线><Route path="/portfolio/:slug" component={PortfolioDetail}/><Route path="*" component={NoMatch}/></路线>

解决方案

无法深入了解,但我能够通过 ComponentWillRecieveProps 实现我的目标:

componentWillReceiveProps(nextProps){if (nextProps.params.slug !== this.props.params.slug) {const {dispatch, params} = nextProps;PortfolioDetail.readyOnActions(dispatch, params, true);}}

换句话说,无论出于何种原因,当我使用 React Router Link 链接到具有相同父组件的页面时,它不会触发 componentWillUnMount/componentWillMount.所以我不得不手动触发我的动作.每当我使用不同的父组件链接到 Routes 时,它都会按我的预期工作.

也许这是设计好的,但它看起来不对,也不直观.我注意到 Stackoverflow 上有很多关于链接更改 url 但不更新页面的类似问题,所以我不是唯一一个.如果有人对此有任何见解,我仍然很乐意听到!

This is driving me crazy. When I try to use React Router's Link within a nested route, the link updates in the browser but the view isn't changing. Yet if I refresh the page to the link, it does. Somehow, the component isn't updating when it should (or at least that's the goal).

Here's what my links look like (prev/next-item are really vars):

<Link to={'/portfolio/previous-item'}>
    <button className="button button-xs">Previous</button>
</Link>
<Link to={'/portfolio/next-item'}>
    <button className="button button-xs">Next</button>
</Link>

A hacky solution is to manaully call a forceUpate() like:

<Link onClick={this.forceUpdate} to={'/portfolio/next-item'}>
    <button className="button button-xs">Next</button>
</Link>

That works, but causes a full page refresh, which I don't want and an error:

ReactComponent.js:85 Uncaught TypeError: Cannot read property 'enqueueForceUpdate' of undefined

I've searched high and low for an answer and the closest I could come is this: https://github.com/reactjs/react-router/issues/880. But it's old and I'm not using the pure render mixin.

Here are my relevant routes:

<Route component={App}>
    <Route path='/' component={Home}>
        <Route path="/index:hashRoute" component={Home} />
    </Route>
    <Route path="/portfolio" component={PortfolioDetail} >
        <Route path="/portfolio/:slug" component={PortfolioItemDetail} />
    </Route>
    <Route path="*" component={NoMatch} />
</Route>

For whatever reason, calling Link is not causing the component to remount which needs to happen in order to fetch the content for the new view. It does call componentDidUpdate, and I'm sure I could check for a url slug change and then trigger my ajax call/view update there, but it seems like this shouldn't be needed.

EDIT (more of the relevant code):

PortfolioDetail.js

import React, {Component} from 'react';
import { browserHistory } from 'react-router'
import {connect} from 'react-redux';
import Loader from '../components/common/loader';
import PortfolioItemDetail from '../components/portfolio-detail/portfolioItemDetail';
import * as portfolioActions  from '../actions/portfolio';

export default class PortfolioDetail extends Component {

    static readyOnActions(dispatch, params) {
        // this action fires when rendering on the server then again with each componentDidMount. 
        // but not firing with Link...
        return Promise.all([
            dispatch(portfolioActions.fetchPortfolioDetailIfNeeded(params.slug))
        ]);
    }

    componentDidMount() {
        // react-router Link is not causing this event to fire
        const {dispatch, params} = this.props;
        PortfolioDetail.readyOnActions(dispatch, params);
    }

    componentWillUnmount() {
        // react-router Link is not causing this event to fire
        this.props.dispatch(portfolioActions.resetPortfolioDetail());
    }

    renderPortfolioItemDetail(browserHistory) {
        const {DetailReadyState, item} = this.props.portfolio;
        if (DetailReadyState === 'WORK_DETAIL_FETCHING') {
            return <Loader />;
        } else if (DetailReadyState === 'WORK_DETAIL_FETCHED') {
            return <PortfolioItemDetail />; // used to have this as this.props.children when the route was nested
        } else if (DetailReadyState === 'WORK_DETAIL_FETCH_FAILED') {
            browserHistory.push('/not-found');
        }
    }

    render() {
        return (
            <div id="interior-page">
                {this.renderPortfolioItemDetail(browserHistory)}
            </div>
        );
    }
}

function mapStateToProps(state) {
    return {
        portfolio: state.portfolio
    };
}
function mapDispatchToProps(dispatch) {
    return {
        dispatch: dispatch
    }
}

export default connect(mapStateToProps, mapDispatchToProps)(PortfolioDetail);

PortfolioItemDetail.js

import React, {Component} from 'react';
import {connect} from 'react-redux';
import Gallery from './gallery';

export default class PortfolioItemDetail extends React.Component {

    makeGallery(gallery) {
        if (gallery) {
            return gallery
                .split('|')
                .map((image, i) => {
                    return <li key={i}><img src={'/images/portfolio/' + image} alt="" /></li>
            })
        }
    }

    render() {
        const { item } = this.props.portfolio;

        return (
            <div className="portfolio-detail container-fluid">
                <Gallery
                    makeGallery={this.makeGallery.bind(this)}
                    item={item}
                />
            </div>
        );
    }
}

function mapStateToProps(state) {
    return {
        portfolio: state.portfolio
    };
}

export default connect(mapStateToProps)(PortfolioItemDetail);

gallery.js

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

const Gallery = (props) => {

    const {gallery, prev, next} = props.item;
    const prevButton = prev ? <Link to={'/portfolio/' + prev}><button className="button button-xs">Previous</button></Link> : '';
    const nextButton = next ? <Link to={'/portfolio/' + next}><button className="button button-xs">Next</button></Link> : '';

    return (
        <div>
            <ul className="gallery">
                {props.makeGallery(gallery)}
            </ul>
            <div className="next-prev-btns">
                {prevButton}
                {nextButton}
            </div>
        </div>
    );
};

export default Gallery;

New routes, based on Anoop's suggestion:

<Route component={App}>
    <Route path='/' component={Home}>
        <Route path="/index:hashRoute" component={Home} />
    </Route>
    <Route path="/portfolio/:slug" component={PortfolioDetail} />
    <Route path="*" component={NoMatch} />
</Route>

解决方案

Could not get to the bottom of this, but I was able to achieve my goals with ComponentWillRecieveProps:

componentWillReceiveProps(nextProps){
    if (nextProps.params.slug !== this.props.params.slug) {
        const {dispatch, params} = nextProps;
        PortfolioDetail.readyOnActions(dispatch, params, true);
    }
}

In other words, for whatever reason when I use React Router Link to link to a page with the SAME PARENT COMPONENT, it doesn't fire componentWillUnMount/componentWillMount. So I'm having to manually trigger my actions. It does work as I expect whenever I link to Routes with a different parent component.

Maybe this is as designed, but it doesn't seem right and isn't intuitive. I've noticed that there are many similar questions on Stackoverflow about Link changing the url but not updating the page so I'm not the only one. If anyone has any insight on this I would still love to hear it!

这篇关于反应路由器链接不会导致组件在嵌套路由内更新的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
相关文章
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆