2016-08-07 3 views
11

이것은 나를 미치게합니다. 중첩 된 경로에서 React Router의 링크를 사용하려고하면 링크가 브라우저에서 업데이트되지만보기는 변경되지 않습니다. 그러나 링크로 페이지를 새로 고치면 그렇게됩니다. 어떻게 든 구성 요소가 업데이트되어야 할 때 업데이트되지 않습니다 (또는 적어도 목표가 됨). 작동리액터 라우터 중첩 루트 내에서 구성 요소가 업데이트되지 않는 링크

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

:

<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 forceUpate 전화 manaully하는 것입니다 : 여기

내 링크 (있는 정말 바르 이전/다음 항목)처럼 무엇 내가 높고 낮은 검색 한

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

:하지만 원하지 않는 전체 페이지 새로 고침, 및 오류가 발생합니다 대답과 가장 가까운 곳은 https://github.com/reactjs/react-router/issues/880입니다. 하지만 그것은 오래되었고 순수한 렌더링 믹스를 사용하지 않고 있습니다. 어떤 이유를 들어

<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> 

, 링크는 새로운 뷰의 컨텐츠를 취득하기 위해 일어날 필요가있는 마운트 할 수있는 구성 요소를 일으키는 원인이되지 전화 : 여기

내 관련 경로입니다. 그것은 componentDidUpdate를 호출하고 URL 슬러그 변경을 확인한 다음 거기에 내 아약스 호출 /보기 업데이트를 트리거 할 수 있지만 이것이 필요하지 않아야하는 것 같습니다.

EDIT (관련 코드 인 이상) :

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; 

새로운 경로 Anoop의 sugge를 기반으로 자리 :

<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> 

답변

2

구성 요소 코드도 공유하는 것이 좋습니다. 그러나, 나는 로컬로 동일한 것을 재현하려고 노력했고 나를 위해 잘 작동하고있다. 다음은 샘플 코드입니다

import { Route, Link } from 'react-router'; 
import React from 'react'; 
import App from '../components/App'; 

const Home = ({ children }) => (
    <div> 
    Hello There Team!!! 
    {children} 
    </div> 
); 

const PortfolioDetail =() => (
    <div> 
    <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> 
    </div> 
); 

const PortfolioItemDetail =() => (
    <div>PortfolioItemDetail</div> 
); 

const NoMatch =() => (
    <div>404</div> 
); 

module.exports = (
    <Route path="/" component={Home}> 
    <Route path='/' component={Home}> 
     <Route path="/index:hashRoute" component={Home} /> 
    </Route> 
    <Route path="/portfolio" component={PortfolioDetail} /> 
    <Route path="/portfolio/:slug" component={PortfolioItemDetail} /> 
    <Route path="*" component={NoMatch} /> 
    </Route> 
); 
+0

전적으로 동의하므로 구성 요소 코드의 관련 부분을 추가해야합니다. 실제 예제를 가져 주셔서 감사합니다.하지만 다음/이전 포트폴리오 항목으로 변경할 때 머리글/바닥 글을 다시 렌더링 할 필요가 없기 때문에 내 필요와 일치하는지 잘 모르겠습니다. 여전히 작동시키려는 정신으로, 당신이 한 일과 유사하게 (즉 포트폴리오 경로가없고 Item 세부 사항 대신 상위 구성 요소를 대상으로 함) 네싱하지 않고 시도했습니다. 그것은 이전과 같이 여전히 작동하지만 구성 요소를 다시 연결하기 위해 링크를 가져 오는 행운이 없습니다. –

+0

이것은 범용 앱이라는 것을 언급 할 가치가 있습니다. 페이지가 비 홈/포트폴리오 경로 (예 : 홈)에 연결되어 있지만 새로 고침하지 않고도 포트폴리오 페이지를 연결할 수없는 경우 잘 작동합니다. 실제 예제는 여기에 있습니다 : https://jasongallagher.org –

+0

이것을 알아 냈습니까? 나는 같은 문제를 겪고있다. 구성 요소에는 자체 "경로"가 있기 때문입니까? 무엇이 그들을 방아쇠를 당기고 새롭게 할 수 있습니까? – dcsan

2

이의 바닥에 가져올 수 없습니다,하지만 난 ComponentWillRecieveProps 내 목표를 달성 할 수 있었다 : 내가 사용하는 어떤 이유, 즉

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

라우터 링크를 클릭하여 동일한 부모 구성 요소가있는 페이지에 연결하면 componentWillUnMount/componentWillMount가 실행되지 않습니다. 그래서 나는 수동으로 내 행동을 유발해야한다. 다른 상위 구성 요소를 사용하여 경로에 연결할 때마다 예상대로 작동합니다.

아마도 이것은 설계된 것처럼 보이지만 옳지 않거나 직관적이지 않습니다. 나는 URL을 변경하지만 페이지를 업데이 트하지 링크에 대한 Stackoverflow에 많은 비슷한 질문이 있다는 것을 알았습니다 그래서 나는 유일한 사람이 아니에요. 누구든지 이것에 대한 통찰력이 있다면 나는 아직도 그것을 듣고 싶어합니다!

0

componentWillReceiveProps가이 답변이지만 약간 성가시다. 루트 구성 요소가 동일하더라도 라우트 변경에 대한 상태 액션을 설정하는 BaseController "개념"을 작성했습니다. 그래서 상상 당신의 경로는 다음과 같습니다 :

<Route path="test" name="test" component={TestController} /> 
<Route path="test/edit(/:id)" name="test" component={TestController} /> 
<Route path="test/anything" name="test" component={TestController} /> 

을 그래서 다음 BaseController는 경로 업데이트를 확인합니다 :

import React from "react"; 

/** 
* conceptual experiment 
* to adapt a controller/action sort of approach 
*/ 
export default class BaseController extends React.Component { 


    /** 
    * setState function as a call back to be set from 
    * every inheriting instance 
    * 
    * @param setStateCallback 
    */ 
    init(setStateCallback) { 
     this.setStateCall = setStateCallback 
     this.setStateCall({action: this.getActionFromPath(this.props.location.pathname)}) 
    } 

    componentWillReceiveProps(nextProps) { 

     if (nextProps.location.pathname != this.props.location.pathname) { 
      this.setStateCall({action: this.getActionFromPath(nextProps.location.pathname)}) 
     } 
    } 

    getActionFromPath(path) { 

     let split = path.split('/') 
     if(split.length == 3 && split[2].length > 0) { 
      return split[2] 
     } else { 
      return 'index' 
     } 

    } 

    render() { 
     return null 
    } 

} 

당신은 그 일에서 상속 할 수 있습니다

가져 오기 "반응"에서 반작용 ; 나는 상태를 관리 할 수 ​​REDUX을 사용하고

componentWillMount() { 
    const { id } = this.props.match.params; 
    this.props.fetchCategory(id); // Fetch data and set state 
} 

componentWillReceiveProps(nextProps) { 
    const { id } = nextProps.match.params; 
    const { category } = nextProps; 

    if(!category) { 
     this.props.fetchCategory(id); // Fetch data and set state 
    } 
} 

을 :이 붙어있어 './BaseController'

export default class TestController extends BaseController { 


    componentWillMount() { 
     /** 
     * convention is to call init to 
     * pass the setState function 
     */ 
     this.init(this.setState) 
    } 

    componentDidUpdate(){ 
     /** 
     * state change due to route change 
     */ 
     console.log(this.state) 
    } 


    getContent(){ 

     switch(this.state.action) { 

      case 'index': 
       return <span> Index action </span> 
      case 'anything': 
       return <span>Anything action route</span> 
      case 'edit': 
       return <span>Edit action route</span> 
      default: 
       return <span>404 I guess</span> 

     } 

    } 

    render() { 

     return (<div> 
        <h1>Test page</h1> 
        <p> 
         {this.getContent()} 
        </p> 
      </div>) 
     } 

} 
0

에서 수입 BaseController들도 다음과 같이 16

내 솔루션이었다 반작용 그러나 개념은 제 생각과 같습니다.

WillMount 메서드에서 정상 상태를 설정하고 WillReceiveProps가 호출되면 상태를 설정하는 메서드를 호출 할 수없는 경우 상태가 업데이트되었는지 확인할 수 있습니다.이 메서드는 사용자의 상태를 다시 렌더링해야합니다. 구성 요소.

관련 문제