2016-08-17 7 views
0

react-router router.listen (...)을 사용하여 route 매개 변수를 가져 오려고하면 오류가 발생합니다. window.location.pathname.split ('route /') [1]을 사용하여 매개 변수를 얻을 수 있습니다. 어떤 팁?react-router : 라우터 수신 이벤트에서 param을 가져 오지 못했습니다.

나는 왜 이런 일이 일어나는지 알아 내려고 노력해 왔습니다. 지금까지 나는 그것이 첫 번째 경로 변경 (URL 변경)에 실패했다는 것을 알아 차렸다. 내 말은, 내 URL 변경이 /param/y에서 /param/x으로 바뀌 었음을 의미한다. 매개 변수는 다시 클릭 할 때만 사용할 수 있습니다. 내 행동이나 구성 요소와 관련이있는 것 같습니까? 또는 리스너가 반응주기에서 어디에 배치됩니까?

잘못된 lifecycle 메서드로 eventListener를 선언 할 것인지 확실하지 않거나; 내가 생각해 봤 듯이, 나는 Routing을 Store에 전달할 것이지만, 나는 생각하기에이 이벤트를 위해 withRouter (Component)를 사용하고있다. 나는 대신 redux에서 라우팅 상태를 사용해야 할 것 같아요. 나는

청취자가 구성 요소

가정 :

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

가 대신 라우터를 듣고, 당신이보고 싶을 수도 : 같이 표시됩니다,

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

export default connect(mapStateToProps)(MyComponent); 

원래의 소스 코드가 변경 : 설명 후 더 나은 다음 코드로 보는 것입니다 withRotuer.

그것은 .. 당신이 당신의 connect에 params 객체에 대한 액세스를

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

을 줄 것이다 그리고 당신은 componentDidMount/componentWillMount를 수신 할 수 및 componentWillReceiveProps(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 피드백을 기반으로 솔루션을 발견했습니다. 나는 withRouter를 사용하여 MyComponent를 랩핑하고 라우터 위치 변경을 청취 할 필요가 있다고 생각했지만 분명히 잘못되었습니다. 그 이유는 Reducer에서 라우팅 매개 변수를 저장하므로 mapStateToProps 중에 언제든지 호출 할 수 있기 때문입니다.

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