Reputation: 2302
The requirement is to:
Toggle to open the rows component
Highlight the selected row with a background color of cyan
However, when I click the toggle button, the App component isn't re-rendered :(
// App.js
import React, { Component } from 'react';
import {observable, action } from 'mobx';
import {observer} from 'mobx-react';
@observer class Rows extends Component {
rows() {
let { store } = this.props;
var rows = [];
for (var i = 4; i > 0; i--) {
rows.push(
<div
style={i == store.selectedRow ? { backgroundColor: "cyan" } : null }
onClick={store.selectRow.bind(this, i)}>
{ i }
</div>
)
}
return rows;
}
render() {
return (<div>{this.rows()}</div>);
}
}
class Store {
constructor(props) {
this.selectRow = this.selectRow.bind(this)
this.toggleSelector = this.toggleSelector.bind(this)
}
@observable showSelector = false;
@observable selectedRow = 4;
@action selectRow(n) {
this.selectedVersion = n;
}
@action toggleSelector() {
this.showSelector = !this.showSelector;
}
}
//edit here
const store = new Store();
@observer class App extends Component {
render() {
return (
<div className="App">
<button onClick={store.toggleSelector}>Toggle Selector</button>
{ store.showSelector ? <Rows store={store}/> : null }
</div>
);
}
}
export default App;
EDIT As per suggestions I've extracted the creation of the store outside of the component.
Upvotes: 1
Views: 2148
Reputation: 2147
Update: You can try to use @action.bound
decorator, and skip bind(this)
altogether in the constructor. My guess is manual binding is messing up the binding (no pun intended).
See action.bound
The action decorator / function follows the normal rules for binding in javascript. However, Mobx 3 introduces action.bound to automatically bind actions to the targeted object. Note that unlike action, (@)action.bound does not take a name parameter, so the name will always be based on the property name to which the action is bound.
Your store is not being observed because it's created inside render()
. You need to create the store and pass it down to your component either via props
or MobX's Provider.
Provider is a component that can pass stores (or other stuff) using React's context mechanism to child components. This is useful if you have things that you don't want to pass through multiple layers of components explicitly.
inject can be used to pick up those stores. It is a higher order component that takes a list of strings and makes those stores available to the wrapped component.
Upvotes: 2
Reputation: 26
Try This.
// App.js
import React, { Component } from 'react';
import {observable, action } from 'mobx';
import {observer} from 'mobx-react';
@observer class Rows extends Component {
rows() {
let { store } = this.props;
var rows = [];
for (var i = 4; i > 0; i--) {
rows.push(
<div
style={i == store.selectedRow ? { backgroundColor: "cyan" } : null }
onClick={store.selectRow.bind(this, i)}>
{ i }
</div>
)
}
return rows;
}
render() {
return (<div>{this.rows()}</div>);
}
}
class Store {
constructor(props) {
this.selectRow = this.selectRow.bind(this)
this.toggleSelector = this.toggleSelector.bind(this)
}
@observable showSelector = false;
@observable selectedRow = 4;
@action selectRow(n) {
this.selectedVersion = n;
}
@action toggleSelector() {
this.toggleSelector = !this.showSelector;
}
}
// defined outside
const store = new Store();
@observer class App extends Component {
render() {
return (
<div className="App">
<button onClick={store.toggleSelector}>Toggle Selector</button>
{ store.showSelector ? <Rows store={store}/> : null }
</div>
);
}
}
export default App;
Upvotes: 0