Reputation: 80505
Let's assume there's a singleton TodosStore
that looks something like this:
class TodosStore {
@observable isLoading;
@observable todos;
@action.bound fetchTodos() {
this.isLoading = true;
return api.getTodos().then(todos => this.todos = todos)
.finally(()=>this.isLoading=false);
}
getTodoById(id) {
return this.todos.find(todo => todo.id === id);
}
}
Let's also assume a single todo detail view that uses the URL for state, e.g. /todos/123
.
@observer class TodoDetail extends React.Component {
constructor(props){
TodosStore.fetchTodos().then(
this.todo = TodosStore.getTodoById(props.subscriptionId);
);
}
render(){
return (
<div>
{TodosStore.isLoading ?
'Loading, please wait...' :
<h1>this.todo.text</h1>
}
</div>
);
}
}
So, this.todo
is obviously not reactive, so setting it will not re-run render()
.
One option is to create an @observable todo;
in the view code, but I'd rather keep mobx helpers out of views.
Is there a better way for doing this, please?
Upvotes: 2
Views: 2137
Reputation: 2053
One solution
One way is to use React's setState method.
constructor(props){
TodosStore.fetchTodos().then({
this.setState({ todo: TodosStore.getTodoById(props.subscriptionId) })
});
}
Second solution
Another way would be to access (dereference) the todos in your component. That way mobx knows that this component needs to be updated, when the todos change.
class TodoStore {
@observable todos = []; //initialize your todos as empty array
}
@observer class TodoDetail extends React.Component {
constructor(props){
TodosStore.fetchTodos();
}
render(){
const todo = TodoStore.todos.find(todo => todo.id === this.props.subscriptionId);
return (
<div>
{TodosStore.isLoading ?
'Loading, please wait...' :
<h1>todo.text</h1>
}
</div>
);
}
}
In this case, everytime your todos array would change, the component would try to find the todo and rerender.
To let mobx know, that your components need to listen to changes of your observable, you must "do" something with this observable.
Either accessing a prop of the observable, or like in this case, call a method (here todos.find()
)
And there is also a third solution
class TodoStore {
@observable todos = []; //initialize your todos as empty array
}
@observer class TodoDetail extends React.Component {
constructor(props){
TodosStore.fetchTodos();
}
render(){
const todo = TodosStore.getTodoById(props.subscriptionId);
return (
<div>
{TodosStore.isLoading ?
'Loading, please wait...' :
<h1>todo.text</h1>
}
</div>
);
}
}
Because you component now gets your todo in the render method,and your component listens for changes on the isLoading prop, your component will be called when isLoading changes.
With this solution, your component rerenders everytime isLoading changes.
As you see, how you access the observable object makes a difference. It depends on under what condition you want your component to rerender.
Upvotes: 5