Reputation: 4685
I have a basic MobX setup in React Native, but my component does not rerender after an observable is getting updated and I can't seem to figure out why.
react-native 0.56.1; react 16.4.1; mobx 4.5.0; mobx-react 5.2.8
Store
class AppStore {
drawer = false;
toggleDrawer = () => {
this.drawer = !this.drawer;
}
}
decorate(AppStore, {
drawer: observable,
toggleDrawer: action
});
const app = new AppStore();
export default app;
Component
class _AppLayout extends React.Component {
constructor(props) {
super(props);
this.state = {
drawerAnimation: new Animated.Value(0)
};
}
UNSAFE_componentWillReceiveProps(nextProps) {
console.log('will not get called');
if (this.props.app.drawer !== nextProps.app.drawer) {
Animated.timing(this.state.drawerAnimation, {
toValue: nextProps.app.drawer === true ? 1 : 0,
duration: 500
}).start();
}
}
render() {
console.log("will only be called on first render");
const translateX = this.state.drawerAnimation.interpolate({
inputRange: [0, 1],
outputRange: [0, -(width - 50)]
});
return (
<Animated.View style={[styles.app, { transform: [{ translateX }] }]}>
<View style={styles.appContent}>
<RouterSwitch />
</View>
<View style={styles.appDrawer} />
</Animated.View>
);
}
}
const AppLayout = inject("app")(observer(_AppLayout));
Trigger (from different component)
<TouchableOpacity
onPress={() => {
app.toggleDrawer();
// will reflect the new value
console.log(app.drawer)
}}
style={styles.toggle}
/>
EDIT:
After some investigation no rerender was triggered because I didn't use the store in the render()
method, only in componentWillReceiveProps
. This seems super weird to me?
When I use the store in render, even by just assigning a variable, it starts working:
const x = this.props.app.drawer === false ? "false" : "true";
Upvotes: 3
Views: 16771
Reputation: 375
As per mobx docs,
The observer function / decorator can be used to turn ReactJS components into reactive components. It wraps the component's render function in mobx.autorun to make sure that any data that is used during the rendering of a component forces a re-rendering upon change. It is available through the separate mobx-react package.
So you need to use the this.props.app.drawer
inside render function of observer component to receive reactions from mobx.
Refer this link for more details about how and when mobx reacts.
Upvotes: 8
Reputation: 2573
You need to use observer
from mobx-react
on your component, also using decorators is the best practice. Also make sure you're using Provider on your Root component
Store
class AppStore {
@observable drawer = false;
@action toggleDrawer = () => {
this.drawer = !this.drawer;
console.log(this.drawer)
}
}
Component
const app = new AppStore();
export default app;
@observer
class AppLayout extends React.Component {
constructor(props) {
super(props);
this.state = {
drawerAnimation: new Animated.Value(0)
};
}
UNSAFE_componentWillReceiveProps(nextProps) {
console.log('will not get called');
if (this.props.app.drawer !== nextProps.app.drawer) {
Animated.timing(this.state.drawerAnimation, {
toValue: nextProps.app.drawer === true ? 1 : 0,
duration: 500
}).start();
}
}
render() {
console.log("will only be called on first render");
const translateX = this.state.drawerAnimation.interpolate({
inputRange: [0, 1],
outputRange: [0, -(width - 50)]
});
return (
<Provider app={app}>
<Animated.View style={[styles.app, { transform: [{ translateX }] }]}>
<View style={styles.appContent}>
<RouterSwitch />
</View>
<View style={styles.appDrawer} />
</Animated.View>
</Provider>
);
}
}
Trigger
<TouchableOpacity
onPress={() => {
app.toggleDrawer();
// will reflect the new value
console.log(app.drawer)
}}
style={styles.toggle}
/>
Upvotes: 3