react-native mobx call action from provider class - react-native

I have set up a very simple mobx store class:
import {observable, action} from 'mobx'
class TestStore {
#observable placeholder = 'Search In Me';
#action changeName(name) {
this.placeholder = name;
}
}
export default new TestStore();
My parent class (below) is where the provider is set. The problem is in this class I also need to track the route name of the current tab that is active. That all works fine - my issue is how do I call the changeName function in the store from the parent class? Currently this.props.store.changeName is returned as undefined.
import React, { Component } from 'react';
import { Root, Tabs } from './config/router';
import { Alert,View } from 'react-native';
import { Provider } from 'mobx-react';
import TestStore from './config/TestStore';
import {observer} from "mobx-react/native";
#observer class App extends Component {
_getCurrentRouteName(navState) {
if (navState.hasOwnProperty('index')) {
this._getCurrentRouteName(navState.routes[navState.index])
} else {
if (navState.routeName==='Tab 1') {
Alert.alert('Tab 1')
this.props.store.changeName('Tab 1')
}
if (navState.routeName==='Tab 2') {
Alert.alert('Tab 2')
}
if (navState.routeName==='Tab 3') {
Alert.alert('Tab 3')
}
if (navState.routeName==='Tab 4') {
Alert.alert('Tab 4')
}
}
}
render() {
return (
<Provider store={TestStore}>
<Root onNavigationStateChange={(prevState, newState) => {
this._getCurrentRouteName(newState)
}} />
</Provider>
)
}
}
export default App;

So I resolved this issue restructuring my app and moving everything up a level - index.js became the following and imported the original classes in index.js back in:
import React, { Component } from 'react';
import { Provider } from 'mobx-react';
import TestStore from './config/TestStore';
import Base from './app';
class App extends Component {
render() {
return (
<Provider store={TestStore}>
<Base />
</Provider>
)
}
}
export default App;

Related

Undefined Unstated Container in a React Native Component using React Navigation

My problem is That I want to access a Container in a component but it seems to be undefined.
undefined alert image
I am using Unstated and as you can see this is my code in the container file (login-container.js):
import { Container } from 'unstated'
class LoginContainer extends Container {
constructor(props){
super(props)
this.state = {
stepNumber: 0,
}
}
}
export default new LoginContainer()
And this is app.js:
import React, { Component } from 'react'
import { createStackNavigator, createSwitchNavigator } from 'react-navigation'
import { Provider } from 'unstated'
import LoginContainer from './containers/login-container'
import Home from './screens/home'
import Splash from './screens/splash'
import Login from './screens/login'
import Intro from './screens/intro'
export default class App extends Component {
render() {
return (
<Provider inject={[LoginContainer]}>
<AuthStack/>
</Provider>
)
}
}
const SplashStack = createStackNavigator(...)
const AppStack = createStackNavigator(...)
const AuthStack = createStackNavigator(
{
Intro: { screen: Intro},
Login: { screen: Login}
},
{
headerMode: "none",
initialRouteName: "Intro"
}
)
const SwitchNavigator = createSwitchNavigator(...)
And this would be login.js:
import React, { Component } from 'react'
import { Text, View } from 'react-native'
export default class Login extends Component {
constructor(props){
super(props)
}
render() {
// const { state: {stepNumber} } = this.props.loginContainer
alert(this.props.LoginContainer)
return (
<View>
<Text> someText </Text>
</View>
)
}
}
I previously tried to use Subscribe component to inject the container to my app but I got the same thing I am getting here.
Using
- react-native 0.58.6
- react-navigation 2.13.0 (due to some bugs in v3)
- unstated 2.1.1
What's really great about Unstated is how simple it is to implement.
Just wrap your render component in Unstated's <Subscribe to></Subscribe> tags and you're good to go. Whenever you setState() in the Container, all Components that Subscribe to it get re-rendered with the Container's updated state property values available to them.
import React, { Component } from 'react';
import { Text, View } from 'react-native';
import { Subscribe } from 'unstated';
import LoginContainer from './containers/login-container';
export default class Login extends Component {
constructor(props){
super(props)
}
render() {
return (
<Subscribe to={[LoginContainer, AnotherContainer]}>
{(container, another) => (
<View>
<Text>{container.state.stepNumber}</Text>
</View>
})
</Subscribe>
);
}
}
UPDATE: Or do it in this HOC way. After creating this:
WithUnstated.js
import React, { PureComponent } from "react";
import { Subscribe } from "unstated";
import DefaultStore from "../store/DefaultStore";
const withUnstated = (
WrappedComponent,
Stores = [DefaultStore],
navigationOptions
) =>
class extends PureComponent {
static navigationOptions = navigationOptions;
render() {
return (
<Subscribe to={Stores}>
{(...stores) => {
const allStores = stores.reduce(
(acc, v) => ({ ...acc, [v.displayName]: { ...v } }),
{}
);
return <WrappedComponent {...allStores} {...this.props} />;
}}
</Subscribe>
);
}
};
export default withUnstated;
Then wrap your component like so:
import React, { Component } from 'react';
import { Text, View } from 'react-native';
import { Subscribe } from 'unstated';
import LoginContainer from './containers/login-container';
import AnotherContainer from './containers/another-container';
class Login extends Component {
constructor(props){
super(props)
}
render() {
const {LoginContainer: container} = this.props;
return (
<View>
<Text>{container.state.stepNumber}</Text>
</View>
);
}
}
export default withUnstated(Login, [LoginContainer, AnotherContainer])

Redux : problem with the store to exchange data

I want to test Redux on my react-native app. I navigate through several Components - I want a component TestRedux updates a value and that another component TestRedux2 see this value using Redux.
I followed several tutorials on Redux and did this:
Actions:
//myApp/redux/Actions/action.js
import { ADD_RES } from "../Constants/action-types";
export function addResa(payload) {
return { type: ADD_RES, payload: payload };
}
Constants:
//myApp/redux/Components/action-types.js
export const ADD_RES = "ADD_RES";
export const DEL_RES = "DEL_RES";
Reducers:
//myApp/redux/Reducers/resaReducer.js
import { ADD_RES } from "../Constants/action-types";
const initialState = {
res: []
};
function resaReducer(state = initialState, action) {
let nextState;
switch (action.type) {
case ADD_RES:
nextState = {
...state,
payload: action.payload
}
return nextState;
default:
return state
}
}
export default resaReducer;
Store:
//myApp/redux/Store/store.js
import { createStore } from "redux";
import resaReducer from "../Reducers/resaReducer";
const Store = createStore(resaReducer);
export default Store;
TestRedux:
//myApp/redux/Components/TestRedux.js
// I use react-navigation to navigate between components. The component App is the first component and then trigger to testRedux
import React from 'react';
import { View, Text, Alert } from 'react-native';
import { ADD_RES } from "../Constants/action-types";
import {addResa} from "../Actions/actions";
import { connect } from 'react-redux'
import { Provider } from 'react-redux'
import Store from '../Store/store'
import App from '../../App';
const mapStateToProps = (state) => {
return state.date
}
export class TestRedux extends React.Component {
render() {
this.props.dispatch(addResa(2));
return (
<View>
<Button
onPress={() => {this.props.navigation.navigate('TestRedux2')}}
title='test'
/>
<Provider store={Store}>
<App/>
</Provider>
</View>
)
}
}
export default connect(mapStateToProps)(TestRedux)
TestRedux2:
//myApp/redux/Components/TestRedux2.js
import { connect } from 'react-redux'
import React from 'react';
import { View, Text, Button, Alert } from 'react-native';
import { ADD_RES } from "../Constants/action-types";
import {addResa} from "../Actions/actions";
import Store from '../Store/store'
const mapStateToProps = (state) => {
return state.date
}
export class TestRedux2 extends React.Component {
render() {
console.log("Value from TestRedux2 is", Store.getState())
return (
<View>
<Text> Hello </Text>
</View>
)
}
}
export default connect(mapStateToProps)(TestRedux2)
Do I use correctly Redux ?
I have the following error: “Invariant Violation: Could not find "store" in the context of "Connect(TestRedux)". Either wrap the root component in a , or pass a custom React context provider to and the corresponding React context consumer to Connect(TestRedux) in connect options.”
This code:
<Provider store={Store}>
<App/>
</Provider>
which is inside your TestRedux, should be inside your index.js file as follows:
render(
<Provider store={Store}>
<App/>
</Provider>,
document.getElementById('root')
)
import of course your store. That is assuming you haven't made any other changes in your initial index.js file.

Injecting Store in React component results in Error

I am trying inject a store to my React Component however I am getting the following error:
Undefined is not a function (evaluating 'decorator(target,property,desc)')
In my App.js I have:
import React, { Component } from 'react';
import PoolComponent from './app/Components/PoolComponent';
import MeasurementsStore from './app/Stores/MeasurementsStore';
export default class PoolApp extends Component {
render() {
return (
<PoolComponent store="MeasurementsStore"/>
);
}
}
In my PoolComponent.js
import React, { Component } from 'react';
import { observer, inject } from 'mobx-react';
import { AppRegistry, Text, View, TextInput , Picker, Button} from 'react-native';
#observer
export default class PoolComponent extends Component {
saveItems() {
console.log('Pressed save');
}
render() {
const store = this.props.store;
return (
<View>
<Text>Selecteer Pool</Text>
<Picker>
<Picker.Item label="Big" value="big"/>
<Picker.Item label="Small" value="small"/>
</Picker>
<Text>PH</Text>
<TextInput/>
<Text>Totaal Chloor</Text>
<TextInput/>
<Text>Vrij Chloor</Text>
<TextInput/>
<Button
title="Learn More"
color="#841584"
accessibilityLabel="Opslaan"
onPress={this.saveItems}
/>
</View>
);
}
}
And in MeasurementsStore.js I have
import {observable, action, computed} from 'mobx-react';
export default class MeasurementsStore {
#observable phValue = 0;
#observable freeChlorine = 0;
#observable totalChlorine = 0;
#observable totalAlkalinity = 0;
#action data(data: Object) {
if (data.phValue) {
this.phValue = data.phValue;
}
if (data.freeChlorine) {
this.freeChlorine = data.freeChlorine;
}
if (data.totalChlorine) {
this.totalChlorine = data.totalChlorine;
}
if (data.totalAlkalinity) {
this.totalAlkalinity = data.totalAlkalinity;
}
}
}
You don't need inject in this case. You are passing the store directly to your PoolComponent, so there is no need for it. You need to change a few things however:
Pass the actual store, not just the store name as a string, in App.js:
import React, { Component } from 'react';
import PoolComponent from './app/Components/PoolComponent';
import measurementsStore from './app/Stores/MeasurementsStore';
export default class PoolApp extends Component {
render() {
return (
<PoolComponent store={measurementsStore}/>
);
}
Import from mobx and export an instance of MeasurementsStore in MeasurementsStore.js:
import {observable, action, computed} from 'mobx';
class MeasurementsStore {
#observable phValue = 0;
#observable freeChlorine = 0;
#observable totalChlorine = 0;
#observable totalAlkalinity = 0;
#action data(data: Object) {
if (data.phValue) {
this.phValue = data.phValue;
}
if (data.freeChlorine) {
this.freeChlorine = data.freeChlorine;
}
if (data.totalChlorine) {
this.totalChlorine = data.totalChlorine;
}
if (data.totalAlkalinity) {
this.totalAlkalinity = data.totalAlkalinity;
}
}
}
const measurementsStore = new MeasurementsStore();
export default measurementsStore;

Set and access mutable app global params with React Navigation in React Native

Is there a way I can declare mutable global parameters within React Navigation StackNavigator? I'm trying to achieve a central point of reference for global variables in the app.
Suppose this is my index.android.js :
import {
AppRegistry
} from 'react-native';
import { StackNavigator } from 'react-navigation';
import {MainScreen} from './components/mainScreen';
import {SegundoScreen} from './components/segundoScreen';
import {TerceraScreen} from './components/terceraScreen';
const SampleAppStack = StackNavigator(
{
Home : { screen : MainScreen },
SegundoScreen : { screen : SegundoScreen },
TerceraScreen : { screen : TerceraScreen }
},
{
headerMode: 'none'
},
{
appGlobalVariables: {
Session : '',
variable1 : 'cool',
variable2 : 'coolant',
variable3 : 'color',
variable4 : 'none',
objetoUno : {},
objetoDos : {},
objetoTres : {}
}
}
);
AppRegistry.registerComponent('SampleApplication', () => SampleAppStack);
Then in directory components/mainScreen.js, I supposedly have...
import React, { Component } from 'react';
import {Text,View} from 'react-native';
import { StackNavigator } from 'react-navigation';
class MainScreen extends Component {
constructor(props) {
super(props);
this.state = {dummyProp:'dummyProp'};
}
render() {
var {appGlobalVariables} = this.props.StackNavigator;
return (
<View>
<Text>Received from App's global variables: {appGlobalVariables.variable1}</Text>
</View>
);
}
}
export {MainScreen}
And in directory components/segundoScreen.js, I supposedly have...
import React, { Component } from 'react';
import {Text,View} from 'react-native';
import { StackNavigator } from 'react-navigation';
class SegundoScreen extends Component {
constructor(props) {
super(props);
this.state = {dummyProp:'dummyProp'};
}
render() {
var {appGlobalVariables} = this.props.StackNavigator;
return (
<View>
<Text>Received from App's global variables: {appGlobalVariables.variable2}</Text>
</View>
);
}
}
export {SegundoScreen}
And in directory components/terceraScreen.js, I supposedly have...
import React, { Component } from 'react';
import {Text,View} from 'react-native';
import { StackNavigator } from 'react-navigation';
class TerceraScreen extends Component {
constructor(props) {
super(props);
this.state = {dummyProp:'dummyProp'};
}
render() {
var {appGlobalVariables} = this.props.StackNavigator;
return (
<View>
<Text>Received from App's global variables: {appGlobalVariables.variable3}</Text>
</View>
);
}
}
export {TerceraScreen}
I've tried it, but it is not working.
An option would be to use global like mentioned here https://stackoverflow.com/a/36994650/4805414. Also see accepted answer to the belonging question. Maybe that's an alternative for you, too.

MobX with React Native: store is undefined

This is my first go at using MobX so this may be a simpler problem than I imagine, but I'm not getting any errors with the things I've tried; the store is simply undefined wherever I try to use it. I've tried both importing the store directly into components and passing props from the main file (also with , but I'm not sure if I used that right). I've experimented with several different .babelrc file settings as well, but that doesn't seem to be an issue.
Here is the UserStore:
import React from 'react';
import { observable } from 'mobx';
class UserStore {
#observable info = {
username: "bob",
password: "secret",
email: "bob#email.com"
}
}
const userStore = new UserStore()
export default userStore;
Here is a simplified App.js:
import React, { Component } from 'react';
import { StyleSheet, Text, View } from 'react-native';
import Profile from './app/Profile.js';
import { UserStore } from './app/UserStore.js';
export default class App extends Component {
constructor(){
super();
this.state = {
page: 'Profile',
}
}
changePage(){
switch (this.state.page) {
case "Profile":
return <Profile logout={this.logout.bind(this)} userStore={UserStore}/>;
}
}
render() {
return (
<View>
{this.changePage()}
</View>
);
}
}
And here is a simplified Profile.js:
import React, { Component } from 'react';
import { StyleSheet, Text, View, Button } from 'react-native';
import { observer } from 'mobx-react/native';
#observer
export default class Profile extends Component {
render() {
console.log(this.props.userStore);
return (
<View>
<Text>Profile Page</Text>
<Text>username: {props from store go here}</Text>
<Text>password: {props from store go here}</Text>
<Text>email: {props from store go here}</Text>
</View>
);
}
}
All I'm trying to do right now is get the pre-defined observable "info" object from the store to the Profile.js component and display that information. This is being way more difficult than it should be - any insight is greatly appreciated!
Since you declared export default userStore; in UserStore.js
Try changing the way you import in App.js by removing the {}:
import UserStore from './app/UserStore.js';
{} is needed only if you want to do a named import. Here is a good read if you want to know more.