[英]Successful dispatch does not cause re-render even though state has been changed
盡管我的狀態成功更新(通過console.log和redux devtools檢查)我無法重新渲染我的視圖
您可以在https://github.com/attendanceproject/djattendance/tree/attendance-redux/ap/static/react-gulp/app上查看代碼
大多數代碼都在scripts文件夾中,與我的問題有關的最重要的部分如下。 每次在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,
}
減速機代碼
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))
。
動作創建者只是通過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
})
通過增加時間來改變原始時刻。
但是我們在Redux文檔中強調減速器必須是純粹的,並且狀態絕不能變異,或者React Redux不會看到變化。 這使Redux變得高效,因為它只重新呈現它知道已經改變的內容。
我建議的解決方案是停止使用Moment.js作為你所在州的一部分。 使用常規的JavaScript Date
對象, 確保永遠不會改變它們 ,並且只在組件的render
方法中使用Moment.js。
最后,傳遞從當前狀態派生的數據是一種反模式。 您的操作目前看起來像這樣:
{type: NEXT_WEEK, date}
但這是太多的信息! reducer已經知道了該州的當前日期,因此無需通過它。
相反,您可以在沒有日期的情況下觸發操作:
{type: NEXT_WEEK}
並教你的減速器在計算新的時候使用當前的日期。
假設您更改了代碼以使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組件以響應這些更改。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.