react-router:从路由器监听事件中获取param失败

时间:2016-08-17 00:50:52

标签: javascript reactjs react-router react-router-redux

我发现在尝试使用react-router router.listen(...)尝试获取路由参数时,它失败了。通过使用window.location.pathname.split(' route /')[1],我可以得到参数。有什么提示吗?

我一直想弄清楚为什么会这样。到目前为止,我注意到它在第一次路线更改时失败了(网址更改) - 我的意思是,通过使用,我的网址从/param/y更改为/param/x;但该参数仅在我再次单击时才可用。我想这可能与我的动作或我的组件有关?或者侦听器在反应生命周期中的位置?

不确定我是否在错误的生命周期方法中声明了eventlistener;正如我一直在想的那样,我将路由传递给Store,但我认为我正在使用withRouter(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;

2 个答案:

答案 0 :(得分:1)

我找到了基于@TryingToImprove反馈的解决方案,我非常感谢。我假设我需要使用withRouter并将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);

答案 1 :(得分:1)

您可能需要查看withRotuer

,而不是收听路由器

它可以让您访问connect ..

中的params对象
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);
    }
}

现在你的组件不需要了解react-router并且更可重复使用,再加上当前的设置,你的组件将永远不会停止监听路由更改(因为缺少&#34; router.removeListner&#34 ;)可能导致问题。

可在此处https://egghead.io/lessons/javascript-redux-using-withrouter-to-inject-the-params-into-connected-components?course=building-react-applications-with-idiomatic-redux

找到解释withRouter的精彩视频