2016-08-17 38 views
0

我發現嘗試使用react-router router.listen(...)獲取路由參數時失敗。通過使用window.location.pathname.split('route /')[1],我可以得到參數。有小費嗎 ?react-router:從路由器偵聽事件獲取參數失敗

我一直在試圖弄清楚爲什麼會發生這種情況。到目前爲止,我注意到它在第一次路由更改(url更改)時失敗 - 我的意思是,通過使用,我的url從/param/y更改爲/param/x;但該參數僅在再次點擊時纔可用。我想這可能與我的行動或我的組件有關?或者聽衆被放置在反應生命週期中的哪個位置?

不知道我是否在錯誤的生命週期方法中聲明瞭eventlistener,或者;正如我一直在想的那樣,我將路由傳遞給Store,但我正在使用Router(Component)來處理這個事件。我想我需要使用redux的路由狀態。我想

具有監聽器組件:

import React, { Component } from 'react'; 
import { connect } from 'react-redux'; 
import { withRouter } from 'react-router'; 
import { setActiveQuestion, setQuestionAnswer } from '../actions/index'; 
import { bindActionCreators } from 'redux'; 
import { Link } from 'react-router'; 
import Navbar from '../containers/navbar'; 

class Question extends Component { 
    constructor(props) { 
     super(props); 
     this.getClassName = this.getClassName.bind(this); 
    } 
    componentWillMount() { 
     this.setEventListeners(); 
    } 

    setEventListeners() { 
     this.props.router.listen(() => { 
      // using location pathname instead, since props.params fail 
      //let question_id = this.props.params.question_id; 
      let question_id = window.location.pathname.split('question/')[1] 
      this.props.setActiveQuestion(question_id); 
     }); 
    } 

    setAnswer(answer_id) { 
     let question_id = this.props.question.id; 
     this.props.setQuestionAnswer(question_id, answer_id); 
    } 

    getClassName(answers, item_answer_id) { 

     let classes = []; 

     // find the answer for the active question 
     let answer_index = _.findIndex(answers, (answer) => { 
      return answer.question_id === this.props.question.id; 
     }); 

     // if there's no answer yet, skip class placement 
     if (answer_index === -1) { 
      return; 
     } 

     let answer = answers[answer_index]; 

     // Test cases 
     const isUserCorrect =() => { 
      return answer.answer_id == answer.correct_answer_id && item_answer_id == answer.correct_answer_id 
     } 

     const isUserAnswer =() => { 
      return answer.answer_id === item_answer_id; 
     } 

     const isCorrectAnswer =() => { 
      return item_answer_id == answer.correct_answer_id; 
     } 

     // Test and set the correct case classname for styling 
     if (isUserCorrect()) { 
      classes.push('user_correct_answer'); 
     } 

     if (isUserAnswer()) { 
      classes.push('user_answer'); 
     } 

     if (isCorrectAnswer()) { 
      classes.push('correct_answer'); 
     } 

     return classes.length > 0 ? classes.join(' ') : ''; 

    } 

    answersList() { 
     return this.props.question.answers.map((answer) => { 
      return <li className={ this.getClassName(this.props.answers, answer.id) } key={ answer.id } onClick={() => this.setAnswer(answer.id) }>{ answer.text }</li> 
     }); 
    } 

    render() { 
     return (
      <div> 
       <div className='question-container'> 
        <h2>{ this.props.question && this.props.question.question }</h2> 
        <ul> 
        { 
         this.props.question && 
         this.answersList() 
        } 
        </ul> 
       </div> 
       <Navbar /> 
      </div> 
     ); 
    } 
} 

function mapStateToProps(state, ownProps) { 
    return { 
     question: state.questions.active, 
     answers: state.answers 
    } 
} 

function matchDispatchToProps(dispatch) { 
    return bindActionCreators({ 
     setActiveQuestion: setActiveQuestion, 
     setQuestionAnswer: setQuestionAnswer 
    }, dispatch); 
} 

export default connect(mapStateToProps, matchDispatchToProps)(withRouter(Question)); 

這裏的減速機:

import { FETCH_QUESTIONS, SET_ACTIVE_QUESTION } from '../actions/index'; 
import _ from 'lodash'; 

const INITIAL_STATE = { 
    loading: true, 
    list: [], 
    active: 0 

}; 

export default function(state = INITIAL_STATE, action) { 

    switch (action.type) { 

     case FETCH_QUESTIONS: 

      return Object.assign({}, state, { 
       loading: false, 
       list: action.payload 
      }); 

     break; 

     case SET_ACTIVE_QUESTION: 

      // retrieve the active question by the route param `question id` 
      let question_id = parseInt(action.payload); 
      let question = _.find(state.list, function (question) { 
       return question.id === question_id; 
      }); 

      return Object.assign({}, state, { 
       active: question 
      }); 

     break; 

     default: 
      return state; 

    } 

}; 

應用程序入口點index.js:

import React from 'react'; 
import ReactDOM from "react-dom"; 
import { Router, browserHistory } from 'react-router'; 
import { syncHistoryWithStore } from 'react-router-redux' 
import { createStore, applyMiddleware } from 'redux'; 
import { Provider } from 'react-redux'; 
import routes from './config/routes'; 
import reducers from './reducers'; 
import promise from 'redux-promise'; 

const createStoreWithMiddleware = applyMiddleware(promise)(createStore); 
const store = createStoreWithMiddleware(reducers); 
const history = syncHistoryWithStore(browserHistory, store); 

ReactDOM.render(
    <Provider store={ store }> 
     <Router history={ history } routes={ routes } /> 
    </Provider>, 
    document.getElementById('app') 
); 

的router.js文件:

import { combineReducers } from 'redux'; 
import questionsReducer from './reducer_questions'; 
import answerReducer from './reducer_answers'; 
import { routerReducer } from 'react-router-redux' 

const rootReducer = combineReducers({ 
    questions: questionsReducer, 
    answers: answerReducer, 
    routing: routerReducer 
}); 

export default rootReducer; 

回答

1

不是聽路由器,你可能想看看在withRotuer

它會給你訪問params對象在connect ..

withRouter(connect(function(state, props) { 
    return { question_id: props.params.question_id }; 
})(MyComponent) 

然後你就可以監聽componentDidMount/componentWillMountcomponentWillReceiveProps(nextProps

componentWillMount() { 
    this.props.setActiveQuestion(this.props.question_id); 
} 

componentWillReceiveProps(nextProps) { 
    if (this.props.question_id != nextProps.question_id) { 
     this.props.setActiveQuestion(nextProps.question_id); 
    } 
} 

現在,您的組件將不會需要了解反應路由器和更多的可重複使用,再加上你目前的設置,你的組件將永遠不會停止監聽路由變化(因爲缺少「router.removeListner」),這可能會導致問題。

一個很好的視頻解釋withRouter可以在這裏找到https://egghead.io/lessons/javascript-redux-using-withrouter-to-inject-the-params-into-connected-components?course=building-react-applications-with-idiomatic-redux

1

我發現基於@TryingToImprove反饋的解決方案,我非常感謝。我認爲我需要使用路由器並將MyComponent包裝進去,然後聽取路由器位置更改,但這顯然是錯誤的;原因是因爲我存儲了來自Reducer的路由參數,所以我可以在mapStateToProps期間隨時調用。然後更好解釋是考慮下面的代碼:

function mapStateToProps(state, ownProps) { 
    return { 
     my_parameter_name: ownProps.params.my_parameter_name 
    } 
} 

export default connect(mapStateToProps)(MyComponent); 

原始源代碼改變,看起來就像這樣:

import React, { Component } from 'react'; 
import { connect } from 'react-redux'; 
import { withRouter } from 'react-router'; 
import { setActiveQuestion, setQuestionAnswer } from '../actions/index'; 
import { bindActionCreators } from 'redux'; 
import { Link } from 'react-router'; 
import Navbar from '../containers/navbar'; 

class Question extends Component { 
    constructor(props) { 
     super(props); 
     this.getClassName = this.getClassName.bind(this); 
    } 

    componentWillMount() { 
     this.props.setActiveQuestion(this.props.question_id); 
    } 

    componentWillReceiveProps(nextProps) { 
     if (this.props.question_id != nextProps.question_id) { 
      this.props.setActiveQuestion(nextProps.question_id); 
     } 
    } 

    setAnswer(answer_id) { 
     let question_id = this.props.question.id; 
     this.props.setQuestionAnswer(question_id, answer_id); 
    } 

    getClassName(answers, item_answer_id) { 

     let classes = []; 

     // find the answer for the active question 
     let answer_index = _.findIndex(answers, (answer) => { 
      return answer.question_id === this.props.question.id; 
     }); 

     // if there's no answer yet, skip class placement 
     if (answer_index === -1) { 
      return; 
     } 

     let answer = answers[answer_index]; 

     // Test cases 
     const isUserCorrect =() => { 
      return answer.answer_id == answer.correct_answer_id && item_answer_id == answer.correct_answer_id 
     } 

     const isUserAnswer =() => { 
      return answer.answer_id === item_answer_id; 
     } 

     const isCorrectAnswer =() => { 
      return item_answer_id == answer.correct_answer_id; 
     } 

     // Test and set the correct case classname for styling 
     if (isUserCorrect()) { 
      classes.push('user_correct_answer'); 
     } 

     if (isUserAnswer()) { 
      classes.push('user_answer'); 
     } 

     if (isCorrectAnswer()) { 
      classes.push('correct_answer'); 
     } 

     return classes.length > 0 ? classes.join(' ') : ''; 

    } 

    answersList() { 
     return this.props.question.answers.map((answer) => { 
      return <li className={ this.getClassName(this.props.answers, answer.id) } key={ answer.id } onClick={() => this.setAnswer(answer.id) }>{ answer.text }</li> 
     }); 
    } 

    render() { 
     return (
      <div> 
       <div className='question-container'> 
        <h2>{ this.props.question && this.props.question.question }</h2> 
        <ul> 
        { 
         this.props.question && 
         this.answersList() 
        } 
        </ul> 
       </div> 
       <Navbar /> 
      </div> 
     ); 
    } 
} 

function mapStateToProps(state, ownProps) { 
    return { 
     question_id: ownProps.params.question_id, 
     question: state.questions.active, 
     answers: state.answers 
    } 
} 

function matchDispatchToProps(dispatch) { 
    return bindActionCreators({ 
     setActiveQuestion: setActiveQuestion, 
     setQuestionAnswer: setQuestionAnswer 
    }, dispatch); 
} 

export default connect(mapStateToProps, matchDispatchToProps)(Question);