尽管我的状态更新成功(通过 console.log 和 redux devtools 检查)我无法让我的 View 重新呈现
您可以在 https://github.com/attendanceproject/djattendance/tree/attendance-redux/ap/static/react-gulp/app 查看代码
大部分代码都在脚本文件夹中,下面是与我的问题有关的最重要的部分。每次在 WeekBar 组件中按下上一个或下一个按钮时,我都会尝试重新呈现,以便其中的日期相应更新。
容器代码
class Attendance extends Component {
render() {
const { dispatch, trainee, date, events, rolls, slips, selectedEvents } = this.props
console.log('this.props', this.props)
return (
<div>
<div>
<Trainee trainee={trainee} />
<WeekBar
date={date}
onPrevClick={date => dispatch(prevWeek(date))}
onNextClick={date => dispatch(nextWeek(date))}/>
</div>
<hr />
</div>
)
}
}
Attendance.propTypes = {
trainee: PropTypes.shape({
name: PropTypes.string,
id: PropTypes.number,
active: PropTypes.bool
}).isRequired,
date: PropTypes.object.isRequired,
events: PropTypes.array.isRequired,
rolls: PropTypes.array.isRequired,
slips: PropTypes.array.isRequired,
selectedEvents: PropTypes.array
}
function select(state) {
return {
trainee: state.trainee,
date: state.date,
events: state.events,
rolls: state.rolls,
slips: state.slips,
selectedEvents: state.selectedEvents,
}
}
export default connect(select)(Attendance)
组件代码
export default class WeekBar extends Component {
render() {
console.log("render props", this.props)
// var startdate = this.props.date.weekday(0).format('M/D/YY');
// var enddate = this.props.date.weekday(6).format('M/D/YY');
return (
<div className="btn-toolbar" role="toolbar">
<div className="controls btn-group">
<button className="btn btn-info"><span className="glyphicon glyphicon-calendar"></span></button>
</div>
<div className="controls btn-group">
<button className="btn btn-default clndr-previous-button" onClick={(e) => this.handlePrev(e)}>Prev</button>
<div className="daterange btn btn-default disabled">
{this.props.date.weekday(0).format('M/D/YY')} to {this.props.date.weekday(6).format('M/D/YY')}
</div>
<button className="btn btn-default clndr-next-button" onClick={(e) => this.handleNext(e)}>Next</button>
</div>
</div>
);
}
handlePrev(e) {
console.log("hello!", e)
this.props.onPrevClick(this.props.date)
}
handleNext(e) {
this.props.onNextClick(this.props.date)
}
}
WeekBar.propTypes = {
onPrevClick: PropTypes.func.isRequired,
onNextClick: PropTypes.func.isRequired,
date: PropTypes.object.isRequired,
}
reducer 代码
var date = moment()
function handleWeek(state = date, action) {
switch (action.type) {
case PREV_WEEK:
console.log('PREV_WEEK')
return Object.assign({}, state, {
date: action.date.add(-7, 'd')
})
case NEXT_WEEK:
return Object.assign({}, state, {
date: action.date.add(7, 'd')
})
default:
return state
}
}
export default handleWeek
最佳答案
我没仔细看,但你好像用的是Moment.js日期作为模型的一部分。具体来说,onNextClick
调度一个操作:dispatch(nextWeek(date))
。
Action 创建者只是传递 Moment.js 日期:
export function nextWeek(date) {
return {type: NEXT_WEEK, date}
}
最后,reducer 通过调用 add
改变日期对象:
return Object.assign({}, state, {
date: action.date.add(7, 'd') // wrong! it's mutating action.date
})
来自 Moment.js add
documentation :
Mutates the original moment by adding time.
但是我们在 Redux 文档中强调 reducer 必须是纯的,并且状态绝不能发生变化,否则 React Redux 将看不到变化。这就是让 Redux 变得高效的原因,因为它只重新呈现它知道已更改的内容。
我建议的解决方案是停止使用 Moment.js 作为状态的一部分。使用常规 JavaScript Date
对象,确保永远不要改变它们,并且只在组件的 render
方法中使用 Moment.js。
最后,从当前状态派生的 Action 中传递数据是一种反模式。您的操作目前看起来像这样:
{type: NEXT_WEEK, date}
但这信息太多了! reducer 已经知道来自状态的当前日期,因此无需传递它。
相反,您可以触发一个没有日期的 Action :
{type: NEXT_WEEK}
并教你的 reducer 在计算新日期时使用当前日期。
假设您更改代码以将 Date
对象保持在状态中,您可以使用 vanilla JS Date API(虽然它不是很好,因为 Date
也是可变的):
// create a copy of the date object
let newDate = new Date(state.date.getTime());
// mutating here is fine: we mutate a new object
newDate.setDate(newDate.getDate() + 7);
return Object.assign({}, state, {
date: newDate
})
或者你可以使用一个很棒的新库,叫做 date-fns它包含不变性:
import addDays from 'date-fns/add_days';
// ...
return Object.assign({}, state, {
date: addDays(state.date, 7) // non mutating! :D
})
如果您注意永远不要改变状态或操作并始终在数据更改时创建新对象,React Redux 将正确更新 React 组件以响应这些更改。
关于javascript - 即使状态已更改,成功调度也不会导致重新渲染,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/34376803/