Despite a successful update of my state (checked through console.log and redux devtools) I cannot get my views to re-render
You can view the code at https://github.com/attendanceproject/djattendance/tree/attendance-redux/ap/static/react-gulp/app
Most of the code is in the scripts folder, the most important parts pertaining to my problem are below. I'm trying to re-render every time the previous or next button is pressed in the WeekBar component so that the date in there updates accordingly.
container code
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)
component code
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 code
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
A second or subsequent render to update the state is called as re-rendering. React components automatically re-render whenever there is a change in their state or props. A simple update of the state, from anywhere in the code, causes all the User Interface (UI) elements to be re-rendered automatically.
When an action is dispatched, useSelector() will do a reference comparison of the previous selector result value and the current result value. If they are different, the component will be forced to re-render. If they are the same, the component will not re-render.
By default, when we call this method, the component re-renders once it receives new props, even though the props have not changed. To prevent the render method from being called, set the return to false, which cancels the render. This method gets called before the component gets rendered.
React-redux component does not rerender on store state change.
I haven’t taken a close look but you seem to be using Moment.js dates as part of your model. Specifically, onNextClick
dispatches an action: dispatch(nextWeek(date))
.
The action creator just passes the Moment.js date along:
export function nextWeek(date) {
return {type: NEXT_WEEK, date}
}
Finally, the reducer mutates the date object by calling add
:
return Object.assign({}, state, {
date: action.date.add(7, 'd') // wrong! it's mutating action.date
})
From Moment.js add
documentation:
Mutates the original moment by adding time.
However we emphasize in Redux documentation that reducers must be pure, and that the state must never be mutated, or React Redux won’t see the changes. This is what enables Redux to be efficient because it only re-renders what it knows to have changed.
The solution I suggest is to stop using Moment.js as a part of your state. Use regular JavaScript Date
objects, make sure to never mutate them, and only use Moment.js inside your components’ render
methods.
Finally, passing data in action derived from the current state is an anti-pattern. Your action currently looks like this:
{type: NEXT_WEEK, date}
But this is too much information! The reducer already knows the current date from the state so there is no need to pass it.
Instead, you can fire an action without the date:
{type: NEXT_WEEK}
and teach your reducer to use the current date when calculating a new one.
Assuming you changed your code to keep a Date
object in the state, you can use vanilla JS Date API (it’s not very nice though because Date
s are also mutable):
// 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
})
Alternatively you can use a wonderful new library called date-fns which embraces immutability:
import addDays from 'date-fns/add_days';
// ...
return Object.assign({}, state, {
date: addDays(state.date, 7) // non mutating! :D
})
If you take care to never mutate the state or the action and always create new objects when data changes, React Redux will correctly update the React component in response to those changes.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With