I use the following pattern in several ponents throughout my app. I notice that many people discourage using forceUpdate(), and some even call it a hack. So how should this be done without using it?
// user.js
export default class User extends EventEmitter {
constructor(args) {
super();
this.id = args.id;
this.isActivated = args.isActivated || false;
}
activate() {
this.isActivated = true;
this.emit('change');
}
}
// UserView.jsx
export default class UserView extends React.Component {
ponentDidMount() {
this.props.user.on('change', this.onUserUpdate);
}
onUserUpdate() {
this.forceUpdate();
}
render (
var user = this.props.user;
return (
<div>
<span>User {user.isActivated ? 'is' : 'is not'} activated.</span>
<button onClick={user.activate()}>Activate</button>
</div>
);
);
}
// app.js
var user = new User({ id: 123 });
ReactDOM.render(<UserView user={user} />, container);
I use the following pattern in several ponents throughout my app. I notice that many people discourage using forceUpdate(), and some even call it a hack. So how should this be done without using it?
// user.js
export default class User extends EventEmitter {
constructor(args) {
super();
this.id = args.id;
this.isActivated = args.isActivated || false;
}
activate() {
this.isActivated = true;
this.emit('change');
}
}
// UserView.jsx
export default class UserView extends React.Component {
ponentDidMount() {
this.props.user.on('change', this.onUserUpdate);
}
onUserUpdate() {
this.forceUpdate();
}
render (
var user = this.props.user;
return (
<div>
<span>User {user.isActivated ? 'is' : 'is not'} activated.</span>
<button onClick={user.activate()}>Activate</button>
</div>
);
);
}
// app.js
var user = new User({ id: 123 });
ReactDOM.render(<UserView user={user} />, container);
Share
Improve this question
edited Oct 28, 2017 at 6:54
torvin
7,1114 gold badges38 silver badges53 bronze badges
asked Feb 25, 2016 at 3:19
1in7billion1in7billion
3412 gold badges5 silver badges11 bronze badges
2 Answers
Reset to default 7React will automatically render the ponent when either the props or state change.
If you have a model with changing properties then you should either keep it in the ponent's local state.
ponentWillMount() {
this.setState({
user: this.props.user
});
}
activator(user) {
return () => {
// mutate the user instance
user.activate();
// update the state to trigger render
this.setState({ user });
};
}
render (
var user = this.state.user;
return (
<div>
<span>User {user.isActivated ? 'is' : 'is not'} activated.</span>
<button onClick={activator(user)}>Activate</button>
</div>
);
)
Even this is a bit hacky and storing unserializable data (prototype relationships, functions) in state isn't great. We're also mutating the object whilst it's in state (eek!).
An (arguably) more elegant way to solve this problem would be to use a simple object without any instance methods to represent users. Then write a set of pure operation functions that return new user objects reflecting the changes.
// user factory
function User(args) {
return {
id: args.id,
isActivated: args.isActivated || false
};
}
function activate(user) {
// returns a new copy with the isActivated property set to true
return Object.assign({}, user, { isActivated: true });
// or with ES2016 object spread
return { ...user, isActivated: true };
}
We can treat these user objects as immutable (freeze them if you want). Then you can modify the activator code to be more functional.
activator(user) {
return () => {
this.setState({ user: activate(user) });
}
}
Now when you click the activate button we update the state of the ponent with a new object and the old one is free to be garbage collected.
Solved using MobX, a reactive paradigm that makes state observable and automatically upates observing ponents.
https://github./mobxjs/mobx