反应路由器:从路由器侦听事件获取参数失败
react-router: get param from the router listen event fails
我发现当尝试使用 react-router
router.listen(...) 获取路由参数时失败了。通过使用 window.location.pathname.split('route/')[1],我可以获得参数。有什么建议吗?
我一直在努力弄清楚为什么会这样。到目前为止,我注意到它在第一次路由更改(url 更改)时失败 - 我的意思是,通过使用,我的 url 从 /param/y
更改为 /param/x
;但该参数仅在我再次单击时可用。我想这可能与我的行为或我的组件有关?或者监听器在反应生命周期中的位置?
不确定我是否在错误的生命周期方法中声明了事件侦听器,或者;正如我一直在想的那样,我正在将路由传递给 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;
我找到了基于@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);
与其收听路由器,不如看看 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);
}
}
现在你的组件不需要知道 react-router 并且更可重用,加上你当前的设置你的组件将永远不会停止监听路由变化(因为缺少“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
我发现当尝试使用 react-router
router.listen(...) 获取路由参数时失败了。通过使用 window.location.pathname.split('route/')[1],我可以获得参数。有什么建议吗?
我一直在努力弄清楚为什么会这样。到目前为止,我注意到它在第一次路由更改(url 更改)时失败 - 我的意思是,通过使用,我的 url 从 /param/y
更改为 /param/x
;但该参数仅在我再次单击时可用。我想这可能与我的行为或我的组件有关?或者监听器在反应生命周期中的位置?
不确定我是否在错误的生命周期方法中声明了事件侦听器,或者;正如我一直在想的那样,我正在将路由传递给 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;
我找到了基于@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);
与其收听路由器,不如看看 withRotuer
。
它将允许您访问 connect
..
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);
}
}
现在你的组件不需要知道 react-router 并且更可重用,加上你当前的设置你的组件将永远不会停止监听路由变化(因为缺少“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