MobX: Observed Component does not rerender after observable change
Solution 1
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.
Solution 2
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}
/>
mxmtsk
Freelance Developer Recently founded my own little agency. Designer & developer of WahlSwiper, a non profit app for finding a political party that matches your personal opinions in Germany.
Updated on June 28, 2022Comments
-
mxmtsk almost 2 years
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 incomponentWillReceiveProps
. 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";
-
mxmtsk over 5 yearsWhy can‘t I use
decorate
? It‘s in the documentation. If you look at my code you see that I‘m using observer. Provider is also in my Root Component -
Tareq El-Masri over 5 yearsAre you using Typescript or Babel?