2016-08-17 39 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);