I am getting an error "MobX injector: Store 'systemStore' is not available! make sure it is provided by some provider. What I really need to do is pass the store to all of my components so that I hace access to them in the this.props.systemStore in like, componentWillMount
import React from 'react';
import { Platform, StatusBar, StyleSheet, View } from 'react-native';
import { AppLoading, Asset, Font } from 'expo';
import { Ionicons } from '#expo/vector-icons';
import {NavigationActions} from 'react-navigation'
import RootNavigation from './navigation/RootNavigation';
import { inject, observer, Provider } from 'mobx-react';
import { observable, action } from "mobx";
import SystemStore from "./stores/SystemStore";
class Main extends React.Component {
render() {
return (
<Provider systemStore={SystemStore} >
<App />
</Provider>
);
}
}
#inject("systemStore")
export default class App extends React.Component {
state = {
isLoadingComplete: false,
};
render() {
if (!this.state.isLoadingComplete && !this.props.skipLoadingScreen) {
return (
<AppLoading
startAsync={this._loadResourcesAsync}
onError={this._handleLoadingError}
onFinish={this._handleFinishLoading}
/>
);
} else {
return (
<View style={styles.container}>
{Platform.OS === 'ios' && <StatusBar barStyle="default" />}
{Platform.OS === 'android' &&
<View style={styles.statusBarUnderlay} />}
<RootNavigation />
</View>
);
}
}
_loadResourcesAsync = async () => {
return Promise.all([
Font.loadAsync([
// This is the font that we are using for our tab bar
Ionicons.font,
// We include SpaceMono because we use it in HomeScreen.js. Feel free
// to remove this if you are not using it in your app
{ 'space-mono': require('./assets/fonts/SpaceMono-Regular.ttf') },
]),
Asset.loadAsync([
require('./assets/images/robot-dev.png'),
require('./assets/images/robot-prod.png'),
]),
]);
};
_handleLoadingError = error => {
console.warn(error);
};
_handleFinishLoading = () => {
this.setState({ isLoadingComplete: true });
};
}
const styles = StyleSheet.create({
container: {
flex: 1,
backgroundColor: '#fff',
},
statusBarUnderlay: {
height: 24,
backgroundColor: 'rgba(0,0,0,0.2)',
},
});
Expo.registerRootComponent(Main);
and the store looks like this
import {observable, action, computed} from 'mobx'
class SystemStore {
#observable loggedIn = false;
#observable controlAuth = false;
#observable testingKey = "Testing-Yo"
}
export default new SystemStore()
i have been all over looking for a solution, just cannot seem to get my head around this one. thanks
So how I deal with this issue is, I create a file called stores.js which looks like this:
import SystemStore from './stores/systemStore';
const systemStore = new SystemStore();
export {
SystemStore
}
export default {
systemStore
}
In this file I import and export all my stores, so that I can always call stores.systemStore (and all other stores you have) with just importing my stores.js like this
import React from 'react';
import {observer} from 'mobx-react';
import stores from './../../stores';
#observer
class TestComponent extends React.Component {
constructor(props){
super(props);
}
render() {
return (
<div>
{stores.systemStore.testingKey}
</div>
);
}
}
export default TestComponent;
And my stores look like this then:
import store from 'store';
import {observable, action, computed} from 'mobx';
class systemStore {
#observable loggedIn = false;
#observable controlAuth = false;
#observable testingKey = "Testing-Yo";
}
export default systemStore;
I hope this will help you. It works for me :)
Related
I've created a React Native app with navigation provided by react-navigation, also integrating redux with react-navigation-redux-helpers, and I'm trying to figure out a good way of implementing a globally-available 'SignOutHeaderButton' component that, when pressed, will dispatch a redux action and perform a navigation operation.
At the moment, I'm having to pass a function via screenProps from the application root component, which is the function that dispatches the redux action. This function is then passed to the UpdatesListView container component via screenProps, and is then passed into the SignOutHeaderButton common component as a prop via navigationOptions.
Is there a better way in which I can implement this so that I don't have to pass any props into the SignOutHeaderButton component and without having to instantiate a signOut() function within each container component in the application from which there will be a 'Sign Out' button?
App.js:
import React from 'react';
import { StyleSheet, Text, View } from 'react-native';
import { reduxifyNavigator } from 'react-navigation-redux-helpers';
import { PersistGate } from 'redux-persist/integration/react';
import { Provider, connect } from 'react-redux';
import { appContainer } from './src/navigators';
import { store, persistor } from './src/store/configureStore';
import { signOut } from './src/actions/auth';
const app = reduxifyNavigator(appContainer, 'root');
const mapStateToProps = state => {
return {
state: state.navReducer
}
}
const AppWithNavigationState = connect(mapStateToProps)(app);
export default class App extends React.Component {
signOut() {
store.dispatch(signOut());
}
render() {
return (
<Provider store={store}>
<PersistGate loading={null} persistor={persistor}>
<AppWithNavigationState
screenProps={{
signOut: this.signOut
}} />
</PersistGate>
</Provider>
)
}
}
UpdatesListView.js:
import React from 'react';
import { View, Container, Content, Text, Button } from 'native-base';
import { connect } from 'react-redux';
import SignOutHeaderButton from '../components/common/SignOutHeaderButton';
class UpdatesListView extends React.Component {
constructor(props) {
super(props);
}
static navigationOptions = ({ navigation }) => {
return {
headerTitle: 'Updates',
headerRight: <SignOutHeaderButton signOut={navigation.getParam('signOut')} />
}
}
componentDidMount() {
this.props.navigation.setParams({
signOut: this.props.screenProps.signOut
})
}
render() {
return (
<Container>
<Text>UpdatesListView</Text>
</Container>
)
}
}
const mapStatetoProps = state => {
return {
updates: state.updatesReducer,
tags: state.tagsReducer
}
}
export default connect(mapStateToProps)(UpdatesListView);
SignOutHeaderButton.js:
import React from 'react';
import { Button } from 'react-native';
import { withNavigation } from 'react-navigation';
class SignOutHeaderButton extends React.Component {
constructor(props) {
super(props);
}
signOut() {
this.props.signOut();
this.props.navigation.navigate('AuthStack');
}
render() {
return (
<Button
title="Sign Out"
onPress={this.signOut} />
)
}
}
export default withNavigation(SignOutHeaderButton);
Use redux connect, the idea of redux connect is to bind the store dispatch state to your component this, When you use redux connect you can access any redux store dispatch and state from anywhere in your application this works for both react and react-native, for example for SignOutHeaderButton.js:
import React from 'react';
import { Button } from 'react-native';
import { connect } from 'react-redux';
import { withNavigation } from 'react-navigation';
class SignOutHeaderButton extends React.Component {
constructor(props) {
super(props);
}
signOut() {
this.dispatch(signOut());
this.props.navigation.navigate('AuthStack');
}
render() {
return (
<Button
title="Sign Out"
onPress={this.signOut} />
)
}
}
export default connect()(withNavigation(SignOutHeaderButton));
Also, you can pass the redux store state to your component by passing a function to your connect(/*your function*/) to resolve the state data you want.
for better understanding, you can try this tutorial: https://blog.logrocket.com/react-redux-connect-when-and-how-to-use-it-f2a1edab2013
Note using nested smart components is very common, redux connect has a very smart algorithm to compare the store state changes, please check this: https://hackernoon.com/why-using-nested-connect-react-redux-components-is-good-bd17997b53d2
In the js file below we create an integer(ttSelectedItem).
How do you use it on another .js file ?
(Without clicking any button)
Is AsyncStorage solving that problem? If it is true, how?
import React, { Component } from 'react';
import {Platform,StyleSheet,Text,View,Image,ImageBackground} from 'react-native';
import Picker from 'react-native-wheel-picker'
var PickerItem = Picker.Item;
var numberList = [];
var ttSelectedItem,
for (let i = 0; i < 41; i++){
numberList.push(i.toString());
}
export default class yks extends Component<{}> {
constructor(props) {
super(props);
this.state = {
ttSelectedItem : 20,
itemList: numberList,
};
}
onPickerSelect (index, selectedItem) {
this.setState({
[selectedItem] : index,
})
}
render () {
return (
<View>
<Picker style={{width: "100%", height: "100%"}}
selectedValue={this.state.ttSelectedItem}
onValueChange={(index) => this.onPickerSelect(index, 'ttSelectedItem')}>
{this.state.itemList.map((value, i) => (
<PickerItem label={value} value={i} key={"money"+value}/>
))}
</Picker>
</View>
);
}
}
You can create a file ttSelectedItem.js and import it in all the components you need.
Example:
//ttSelectedItem.js
const ttSelectedItem = 'Hello';
export default ttSelectedItem
//YourComponent.js
import ttSelectedItem from './path-to-ttSelectedItem';
class YourComponent extends React.Component {
console.log(ttSelectedItem); // print Hello
}
More info: https://developer.mozilla.org/en-US/docs/web/javascript/reference/statements/export
You can also pass down the prop from a parent component to its children.
Example:
// App.js
import FirstComponent from 'path-to-first-component';
import SecondComponent from 'path-to-second-component';
class App extends React.Component {
render() {
return (
<View>
<FirstComponent ttSelectedItem={'Hello'} />
<SecondComponent ttSelectedItem={'Hello'} />
</View>
)
}
}
// FirstComponent.js
class FirstComponent extends React.Component {
console.log(this.props.ttSelectedItem) //print Hello
}
export default FirstComponent
// SecondComponent.js
const SecondComponent = (props) => {
console.log(props.ttSelectedItem) //print Hello
}
export default SecondComponent
Depending on how complex your code will be, you can use HOCs to wire up some data and pass down your components
Example:
//ttSelectedItem.js
const ttSelectedItem = (Component) => {
return <Component ttSelectedItem={'Hello'} />
}
export default ttSelectedItem;
//YourComponent.js
import ttSelectedItem from 'path-to-ttSelectedItem';
class YourComponent extends Component{
(...)
console.log(this.props.ttSelectedItem); //print Hello
(...)
}
export default ttSelectedItem(YourComponent);
More detail: https://reactjs.org/docs/higher-order-components.html
Or if you need an even complex code, you can use Redux Store to keep this data
Example using Redux and ReduxThunk:
//App.js
import ReduxThunk from 'redux-thunk';
import { Provider } from 'react-redux';
import { createStore, applyMiddleware } from 'redux';
import reducer from 'path-to-your-reducer';
import YourComponent from 'path-to-your-component';
class App extends Component {
render() {
const store = createStore(reducer, {}, applyMiddleware(ReduxThunk));
return (
<Provider store={store}>
<YourComponent />
</Provider>
);
}
}
// YourComponent.js
import { connect } from 'react-redux';
class YourComponent extends React.Component {
console.log(this.props.ttSelectedItem) // prints Hello
}
const mapStateToProps = function(state){
return {
ttSelectedItem: state.ttSelectedItem,
}
}
export default connect(mapStateToProps, {})(MainAppContainer)
// Reducer.js
const INITIAL_STATE = {
ttSelectedItem: 'Hello',
};
export default (state = INITIAL_STATE) => {
return state;
};
More info: https://redux.js.org/basics/store
The last example is just to show another way to handle data between components using Redux. It should be used only when dealing with really complex data sharing.
I'd suggest you to just follow the first example, it might be enough
Hope it helps
Could please somebody help me to manage with StackNavigator and Redux integration? Everything looks pretty simple, but doesn't work.
index.ios.js
import React from 'react'
import {
AppRegistry
} from 'react-native'
import { Provider } from 'react-redux'
import configureStore from './configureStore'
import { StackNavigator } from 'react-navigation';
import Welcome from "./screens/welcome";
import Accounts from "./screens/accounts";
const store = configureStore()
const Nav = StackNavigator({
Welcome: {
screen: Welcome,
},
Accounts: {
screen: Accounts,
},
});
const TestApp = () => (
<Provider store={store}>
<Nav />
</Provider>
)
AppRegistry.registerComponent('TestApp', () => RNRedux)
configureStore.js
import { createStore } from 'redux'
import rootReducer from './reducers'
export default function configureStore() {
let store = createStore(rootReducer)
return store
}
reducers/index.js
import { combineReducers } from 'redux'
import accounts from './accounts'
const rootReducer = combineReducers({
accounts
})
export default rootReducer
screens/accounts.js
import React from 'react';
import { StyleSheet, Text, View, button } from 'react-native';
import { ListItem } from 'react-native-elements';
import { StackNavigator } from 'react-navigation';
import { connect } from 'react-redux';
export class Accounts extends React.Component {
static navigationOptions = {
title: 'Accounts',
}
constructor(props) {
super(props);
}
render() {
state = {}
const { navigate } = this.props.navigation;
return (
<View>
{
this.props.accounts.map((l, i) => (
<ListItem
key={i}
title={l.title}
subtitle={l.hash}
/>
))
}
</View>
);
}
}
function mapStateToProps (state) {
return {
accounts: state.accounts
}
}
export default connect(
mapStateToProps,
)(Accounts)
reducers/accounts.js
const initialState = {
accounts: [
{
title: "Bitcoin Slash Fund",
hash: "0x83247jfy344fgg",
},
],
}
export default function (state = initialState, action) {
switch (action.type) {
default:
return state;
}
}
When I navigate to Accounts screen, I get the error
TypeError: undefined is not a function (near '...this.props.state.accounts.map...');
Accounts screen seems not to be actually connected to Redux, and I can't figure out why. Any ideas? Thanks a lot.
Try this one:
function mapStateToProps (state) {
// Just FYI here you can use console.log to see the state)
// console.log(state);
return {
accounts: state.accounts.accounts
}
}
you could see console.log output in Xcode or in AndroidStudio.
Don't use Redux to manage the state of navigator. refer to this official doc: https://reactnavigation.org/docs/en/redux-integration.html
In my case, if you just want the title of navigator changed when the Redux state changes, just use screenProps, refer to: Passing screenProps to a tab navigator
e.g. in your App.js render method, pass screenProps into the target component,
import { LoginStackNavigator } from './navigators/LoginStackNavigator'
render () {
return (
<LoginStackNavigator screenProps={
{
// Step1. pass the value into the navigator component instance.
tradeTitle: i18n.t('trade', {locale: this.props.language}),
}
}/>
)
}
and in the related file, ( such as LoginStackNavigator.js in my case),
import { AppTabNavigator } from '../navigators/AppTabNavigator'
export const LoginStackNavigator = createStackNavigator({
Trade: {
screen: AppTabNavigator
}
//...
and in the final file ( AppTabNavigator)
export const AppTabNavigator = createBottomTabNavigator({
TradeStackNavigator: {
screen: TradeStackNavigator,
navigationOptions: ({navigation, navigationOptions, screenProps}) => {
return {
// Step2. here use screenProps to retrieve the value passed in .
tabBarLabel: screenProps.tradeTitle,
}
}
I think I'm close with setting up React Redux for my React native app. I currently have this set up.
Here I define the action that I want to call.
/* actions/mapActions.js */
export const setMarker = selectedMarker => {
return {
type: 'SET_MARKER',
selectedMarker
}
}
Here I define the container for the component that I want to use the store in.
//containers/mapContainers.js
import { connect } from 'react-redux';
import { setMarker } from './actions/mapActions'
import HomeScreen from './screens/HomeScreen'
const mapStateToProps = state => {
return {
selectedMarker: state.marker
}
}
const mapDispatchToProps = dispatch => {
return {
markerClick: (marker) => {
dispatch(setMarker(marker))
}
}
}
export default connect(mapStateToProps, mapDispatchToProps)(HomeScreen)
Here I combine the reducers as I've seen in a tutorial I've been following.
//reducers/index.js
import { combineReducers } from 'redux'
import mapReducer from './mapReducer'
const dabApp = combineReducers({
mapReducer
})
export default dabApp
Here I define the reducer for the component.
//reducers/mapReducers.js
const mapReducer = (state = [], action) => {
switch (action.type) {
case 'SET_MARKER':
return [
...state,
{
marker: action.marker
}
]
default:
return state
}
}
export default mapReducer
Main entry point to the application.
//App.js
// other imports here
import { Provider } from 'react-redux';
import { createStore } from 'redux';
import snapApp from './reducers';
let store = createStore(dabApp);
export default class App extends React.Component {
state = {
isLoadingComplete: false,
};
render() {
return (
<Provider store={store}>
<View style={styles.container}>
{Platform.OS === 'ios' && <StatusBar barStyle="default" />}
{Platform.OS === 'android' &&
<View style={styles.statusBarUnderlay} />}
<RootNavigation />
</View>
</Provider>
);
}
}
const styles = StyleSheet.create({
//Styles.
});
Here I define the component.
//Standard react imports.
import { MapView } from 'expo';
import { connect } from 'react-redux';
export default class HomeScreen extends React.Component {
constructor(props) {
super(props);
this.state = {
//Set states.
};
}
render() {
return (
<View style={styles.container}>
<MapView
//MapView info
>
{this.state.markers.map((marker) =>
<MapView.Marker
key={marker.id}
coordinate={marker.coordinate}
onPress={() => {this.props.markerClick(marker); this.props.navigation.navigate('Information');}}>
</MapView.Marker>
)}
</MapView>
</View>
);
}
}
const styles = StyleSheet.create({
//Styles.
});
The error I get is that the function 'markerClick' is undefined in the Map.Marker onPress prop. I have followed the tutorial religiously and can't find the solution to this.
The tutorial that I was following was this one on the official redux site. http://redux.js.org/docs/basics/ExampleTodoList.html.
Has anyone encountered the same issue?
Unfortunately, Harry's answer hasn't solved the issue.
I console.log(this.props) and I get this:
Still is undefined. When I console.log(this.props") I get:
Object {
"navigation": Object {
"dispatch": [Function anonymous],
"goBack": [Function goBack],
"navigate": [Function navigate],
"setParams": [Function setParams],
"state": Object {
"key": "Home",
"routeName": "Home",
},
},
"screenProps": undefined,
"selectedMarker": [Function dispatch],
"type": "SET_MARKER",
}
so I don't even see the function on my props.
As you can see, the function is not defined on this.props.
Thanks,
I feel like you're making more steps than needed.
Try something like this:
import React, { Component } from 'react';
import { MapView } from 'expo';
import { connect } from 'react-redux';
import { View, StyleSheet } from 'react-native';
import { setMarker } from './actions/mapActions'
class HomeScreen extends Component {
onPress(marker) {
this.props.setMarker(marker);
this.props.navigation.navigate('Information');
}
render() {
return (
<View style={styles.container}>
<MapView>
{this.state.markers.map((marker) => (
<MapView.Marker
key={marker.id}
coordinate={marker.coordinate}
onPress={() => { this.onPress(marker); }}
/>
)
)}
</MapView>
</View>
);
}
}
const styles = StyleSheet.create({
//Styles.
});
export default connect(null, setMarker)(HomeScreen);
You don't need to define a function that then dispatches an action, you can just connect the action to the component.
It's also much nicer to just have everything in the same file instead of having a separate mapContainers.js
I´m pretty new to React and Redux and have some issue during my first steps with it. I tried to follow the examples in the Redux Doc´s, but it´s hard for me to understand everything, because every example is jumping between ES5 - 6 or even 7 syntax.
However, When I try to dispatch an action I got the following error
Uncaught TypeError: (0 , _index2.default) is not a function
Error Message
I know that SO Community doesn´t prefer so much code within one Question, but I don´t know where the problem is coming from. Sorry for that!
These is my Code:
Index.js
import 'babel-polyfill'
import React from 'react'
import { render } from 'react-dom'
import { Provider } from 'react-redux'
import App from './containers/App'
import configureStore from './store/configureStore'
const store = configureStore()
render(
<Provider store={store}>
<App />
</Provider>,
document.getElementById('root')
)
My Store
import { createStore, applyMiddleware } from 'redux'
import thunkMiddleware from 'redux-thunk'
import createLogger from 'redux-logger'
import index from '../reducers'
export default function configureStore(preloadedState) {
const store = createStore(
index,
preloadedState,
applyMiddleware(thunkMiddleware, createLogger())
)
if (module.hot) {
// Enable Webpack hot module replacement for reducers
module.hot.accept('../reducers', () => {
const nextRootReducer = require('../reducers').default
store.replaceReducer(nextRootReducer)
})
}
return store
}
My Container Component
import React, { Component, PropTypes } from 'react'
import AddTodo from '../components/AddTodo'
import { connect } from 'react-redux'
import addItem from '../actions/index'
class App extends Component {
constructor(props) {
super(props)
this.handleClick = this.handleClick.bind(this)
}
handleClick(e){
console.log("click")
console.log(e);
const {dispatch} = this.props
dispatch(addItem(e));
}
render() {
return (
<div>
< h1 > Hallo </h1>
<AddTodo handleAddItem={this.handleClick}/>
</div>
)
}
}
App.propTypes = {
dispatch: PropTypes.func.isRequired
}
function mapStateToProps(state){
return {
AddTodo
}
}
export default connect (mapStateToProps)(App)
My Child Component:
import React, { Component, PropTypes } from 'react'
import addItem from '../actions/index'
export default class AddTodo extends Component {
constructor(props) {
super(props)
this.handleClick = this.handleClick.bind(this)
this.state = {newItem: ''}
}
onChange(e){
console.log("change")
console.log(e.target.value);
this.setState({newItem: e.target.value})
}
handleClick(e){
this.props.handleAddItem(this.state.newItem)
// const {dispatch} = this.props
// console.log("clickc")
// console.log(this.state.newItem);
// dispatch(addItem(this.state.newItem))
}
render() {
return (
<div>
<h3>Add Item </h3>
<input
type="text"
value={this.state.newItem}
onChange={this.onChange.bind(this)}
/>
<button onClick={this.handleClick}>Hallo</button>
</div>
)
}
}
The Reducer
export default (state = [], action) => {
switch (action.type){
case 'ADD_ITEM':
return action.item
}
}
And Finally the action
export function addItem(item){
console.log("addTOdo")
return {
type: 'ADD_ITEM',
item
}
}
I hope someone can help me here, sitting since several hours and don´t understand what is happening.
You are not exporting action creator as default. You need either
export default function addItem(item){
console.log("addTOdo")
return {
type: 'ADD_ITEM',
item
}
}
or
import {addItem} from '../actions/index'