I cannot get the store reducer initialState, so I cannot map to props in the component as well.
Here is my reducer:
const initialState = { currentUser: null }
export default function UserReducer(state = initialState, action){
let nextState;
switch(action.type){
case "USER_CONNECTED":
nextState = {
...state,
currentUser : action.value
}
return nextState;
case "USER_DECONNECTED":
nextState = {
...state,
currentUser : null
}
return nextState;
default:
return state;
}
}
Here is the class that configures the store:
import { createStore, combineReducers } from 'redux';
import UserReducer from './reducers/userReducer'
const rootReducer = combineReducers({
currentUser : UserReducer
});
const configureStore = () => {
return createStore(rootReducer);
}
export default configureStore;
And here is where I initialise the store and pass it to the App thanks to a provider:
import {AppRegistry} from 'react-native';
import React from 'react';
import App from './App';
import {name as appName} from './app.json';
import { Provider } from 'react-redux';
import configureStore from './store/store';
const Store = configureStore();
console.log("STORE :"+ JSON.stringify(Store.getState()));
const RNRedux = () => (
<Provider store = { Store }>
<App />
</Provider>
)
AppRegistry.registerComponent(appName, () => RNRedux);
When I print the "STORE" above, it gives me the right output { currentUser : ...}. Then I connect App.js to the store as follows:
const AppNavigator = createStackNavigator(
{
NewAccount: NewAccountScreen,
Login: LoginScreen
},
{
initialRouteName: "Login"
}
);
const AppContainer = createAppContainer(AppNavigator);
export class App extends React.Component {
constructor(props, context){
super(props, context);
}
render() {
console.log("APP.JS : "+ JSON.stringify(this.props));
return (
<AppContainer />
)
}
}
export default connect()(App);
So at the last line I connect the entire App state to the component props but it gives me {}.
You are missing mapStateToProps param in your connect call.
export default connect()(App);
You need to specify mapping function that will take parts of state and pass it to components props.
To map entire state to props try this:
export default connect(state=>state)(App)
Better practice would be to pass only parts of state that the component needs. That way you would avoid unnecessary re-renders when some other part of state changes. For example if your connected component only needs user first name you could do this:
export default connect(state=>{firstName:state.currentUser.firstName})(App)
Related
I get the following error using redux in native react
Invariant Violation: Invariant Violation: Could not find "store" in
the context of "Connect(Lista)". Either wrap the root component in a
, or pass a custom React context provider to and
the corresponding React context consumer to Connect(Lista) in connect
options.
My code is the following
SettingStore
import {createStore, applyMiddleware} from 'redux';
import Reducers from './Reducer'
import thunk from 'redux-thunk'
export default SettingStore = () => {
let store = createStore(Reducers, applyMiddleware(thunk))
return store
}
my index reducer
import {combineReducers} from 'redux';
import loginreducer from './login.reducer'
export default combineReducers({
login: loginreducer,
})
my index action
import {FETCHING_GETDATA_PARVU} from '../Constante'
import {GetParv} from '../Api/Parvularia.api'
export const getParvuSuccess = (data) => {
return {type: FETCHING_GETDATA_PARVU, data}
}
export const GetParvu = () => {
return (dispatch) => {
dispatch(getData())
GetParv(1)
.then(([response, json]) => {
dispatch(getParvuSuccess(json))
})
.catch((error) => console.log(error))
}
}
This is the maintab.reducer of my reducer
import {FETCHING_GETDATA_PARVU} from '../Constante'
const initialState = {
data: [],
isFeching: false,
error: false
}
export default dataReducer = (state = initialState, action) => {
switch(action.type) {
case FETCHING_GETDATA_PARVU:
return {
...state,
data: action.data,
isFeching: false
}
default:
return state
}
}
and let's say this is my app.js of the redux structure
import React, { Component } from 'react';
import { Platform, StyleSheet, Text, View } from 'react-native';
import {Provider} from 'react-redux';
import Lista from '../screens/Educadora/Lista';
import SettingStore from './SettingStore'
let store = SettingStore()
const Ap = () => (
<Provider store={store}>
<Lista/>
</Provider>
)
export default Ap;
This last part is the one that generates me more doubt, I think that this is my error but I do not know why I am new in react native
Edit
This is where I want to show the query made with redux
import { connect } from 'react-redux'
import {GetParvu} from '../../Redux/Actions'
class Lista extends React.Component {
componentWillMount() {
this.props.GetParvu()
render(){
return(algoaca)
}
}
}
const mapStateToProps = state => {
return {
parvu: state.data
}
}
const mapDispatchToProps = dispatch => {
return {
GetParvu: () => {
return dispatch(GetParvu(1))
}
}
}
AppRegistry.registerComponent('EqualsMobile', () => Lista);
export default connect(mapStateToProps, mapDispatchToProps)(Lista);
You have connecting your component in wrong way. connect() is high order component that take mapStateToProps as an argument to map store to your component.
You can use it in your component like this.
function mapStateToProps(state){
return {state}
}
export default connect(mapStateToProps)(your_component_name);
I'm building my first react native app and I encountered a problem to connect to redux store (I also do not have much experience with redux yet). I am using expo.
The error is:
Invariant Violation: Could not find "store" in the context of "Connect(App)". Either wrap the root component in a , or pass a custom React context provider to and the corresponding React context consumer to Connect(App) in connect options.
This error is located at:
in Connect(App) (at withExpoRoot.js:22)
(...)
Here is my code:
Could you please help?
// App.js
import React, { Component } from "react";
import AppStackNav from "./navigators/AppStackNav";
import { Provider, connect } from 'react-redux';
import { createStore } from 'redux';
import guestsReducer from "./reducers/GuestsReducer";
const store = createStore(guestsReducer);
class App extends Component {
constructor(props) {
super(props);
}
addGuest = (index) => {
// ...
}
render() {
return (
<Provider store={store}>
<AppStackNav
screenProps={{
currentGuests: this.state.currentGuests,
possibleGuests: this.state.possibleGuests,
addGuest: this.addGuest
}}
/>
</Provider>
)
}
}
const mapStateToProps = state => {
return {
currentGuests: this.state.current,
possibleGuests: this.state.possible,
addGuest: this.addGuest
};
}
export default connect(mapStateToProps)(App);
// GuestsReducer.js
import { combineReducers } from 'redux';
const INITIAL_STATE = {
current: 10,
possible: [
'Guest1',
'Guest2',
'Guest3',
'Guest4',
],
};
const guestsReducer = (state = INITIAL_STATE, action) => {
switch (action.type) {
default:
return state
}
};
export default combineReducers({
guests: guestsReducer,
});
// AppStackNav.js
import { createStackNavigator, createAppContainer } from "react-navigation";
import Home from "../screens/Home";
import Dashboard from "../screens/Dashboard";
import Project from "../screens/Project";
import Placeholder from "../screens/Placeholder";
const AppStackNav = createStackNavigator({
// ...
});
export default createAppContainer(AppStackNav);
First Issue
const mapStateToProps = ({ guests }) => {
return {
currentGuests: guests.current,
possibleGuests: guests.possible
};
}
Second Issue
You wire redux store to your upper level component which is the App component ... and then use connect and mapStateToProps to access redux store in the children of this upper level component (App) ... I mean you connect your store via mapStateToProps to your AppStackNav component not the App component
const AppStackNav = ({ currentGuests, possibleGuests }) => {
const Stack = createStackNavigator({...});
return <Stack />;
};
const mapStateToProps = ({ guests }) => {
return {
currentGuests: guests.current,
possibleGuests: guests.possible
};
}
// react-navigation v2 is needed for this to work:
export default connect(mapStateToProps)(AppStackNav);
App.js
class App extends Component {
constructor(props) {
super(props);
}
addGuest = (index) => {
// ...
}
render() {
return (
<Provider store={store}>
<AppStackNav />
</Provider>
)
}
}
export default App;
you can't use 'this' keyword outside the class as It wont be able to understand the context for that particular method.
you need to simply remove this keyword from mapStateToProps
like this:
const mapStateToProps = state => {
return {
currentGuests: state.current,
possibleGuests: state.possible
};
}
I am new to react-native and I am trying to implement a simple application using StackNavigation and react-redux with welcome and signup screens. I have configured both the screens using StackNavigation but for some reasons , only the SignUp screen pops up when the app starts. Below are my files :
Index.js
import { AppRegistry } from 'react-native';
import App from './App';
AppRegistry.registerComponent('MyApp', () => App);
App.js
import React, { Component } from 'react';
import { Provider, connect } from "react-redux";
import { addNavigationHelpers } from "react-navigation";
import StackNavConfig from "./js/config/routes";
import getStore from "./js/store";
const AppNavigator = StackNavConfig;
const initialState = AppNavigator.router.getActionForPathAndParams('Welcome');
const navReducer = (state = initialState, action) => {
const newState = AppNavigator.router.getStateForAction(action, state);
return newState || state;
};
const AppWithNavigationState = connect(state => ({
nav: state.nav,
}))(({ dispatch, nav }) => (
<AppNavigator navigation={addNavigationHelpers({ dispatch, state: nav })} />
));
const store = getStore(navReducer);
class App extends React.Component {
render() {
return (
<Provider store={store}>
<AppWithNavigationState />
</Provider>
);
}
}
export default App;
js/config/routes.js
import Welcome from "../components/Welcome/view/Welcome";
import SignUp from "../components/SignUp/view/SignUp";
import { StackNavigator } from "react-navigation";
const Routes = {
Welcome: { screen: Welcome , path: ''},
SignUp: { screen: SignUp , path : '/signup'},
};
const RoutesConfig = {
initialRouteName: 'Welcome',
headerMode: 'none',
};
export default StackNavConfig = StackNavigator(Routes, RoutesConfig);
store.js
import { createStore, applyMiddleware } from "redux";
import thunk from "redux-thunk";
import getRootReducer from "./reducers/index";
export default function getStore(navReducer) {
const store = createStore(
getRootReducer(navReducer),
undefined,
applyMiddleware(thunk)
);
return store;
}
Below are my components
Welcome.js
import React from 'react';
import {
View,
Image} from 'react-native';
import { connect } from "react-redux";
import { bindActionCreators } from "redux";
import * as welcomeActions from "../actions/WelcomeActions";
import { welcomeStyles } from '../styles/WelcomeStyles';
class Welcome extends React.Component {
constructor(){
super();
this.state = { };
}
render(){
return (
<View style = {welcomeStyles.mainContainer}>
<Text>Welcome</Text>
</View>
);
}
}
export default connect(
state => ({
}),
dispatch => bindActionCreators(welcomeActions, dispatch)
)(Welcome);
SignUp.js
import React from 'react';
import {
View,
Image} from 'react-native';
import { connect } from "react-redux";
import { bindActionCreators } from "redux";
import * as welcomeActions from "../actions/SignUpActions";
import { signUpStyles } from '../styles/SignUpStyles';
class Welcome extends React.Component {
constructor(){
super();
this.state = { };
}
render(){
return (
<View style = {signUpStyles.mainContainer}>
<Text>SignUp</Text>
</View>
);
}
}
export default connect(
state => ({
}),
dispatch => bindActionCreators(signUpActions, dispatch)
)(SignUp);
I also have action and reducer files for each of my component.But they are blank as of now , since I haven't yet implemented the redux part.I am combining the reducers as below.
import { combineReducers } from "redux";
import welcomeReducer from "../components/Welcome/reducers/WelcomeReducer";
import signUpReducer from "../components/SignUp/reducers/SignUpReducer";
export default function getRootReducer(navReducer) {
return combineReducers({
nav: navReducer,
welcomeReducer : welcomeReducer,
signUpReducer : signUpReducer,
});
}
As mentioned before , even after setting the initialRouteName to Welcome in my routes.js , the SignUp screen appears first everytime I launch the app. Please help
I found out what was the issue. I was calling this.props.navigate inside the render function by mistake which was causing the navigation to different screen.
i am using react-navigation and redux in react-native.when i just used redux, the states can send to child by redux. but it don't work when i add react-navigation.
my navigation.js
import {StackNavigator} from 'react-navigation';
import Home from './modules/home/views/Home'
export const StackRouter = StackNavigator({
Main: {screen: Home},
initialRouteName: {screen: Home}
});
const firstAction = StackRouter.router.getActionForPathAndParams('Main');
const tempNavState = StackRouter.router.getStateForAction(firstAction);
const initialNavState = StackRouter.router.getStateForAction(
firstAction,
tempNavState
);
//navigationreducer
export const stackReducer = (state=initialNavState,action) => {
debugger
const newState = StackRouter.router.getStateForAction(action, state);
return newState || state;
};
my store.js
import React, {Component} from 'react';
import { connect, Provider } from 'react-redux';
import {createStore, combineReducers, bindActionCreators, applyMiddleware } from 'redux';
import {addNavigationHelpers} from 'react-navigation'
import thunk from 'redux-thunk'
import PropTypes from 'prop-types'
import {StackRouter, stackReducer} from './router'
import home from './modules/home';
//routerConmponent
class RouterAppWithState extends Component {
constructor(props){
super(props)
}
render() {
return (
<StackRouter navigation={addNavigationHelpers({
dispatch: this.props.dispatch,
state: this.props.router,
})} />
);
}
}
//all reducer
const reducer = combineReducers({
home: home.reducer,
router: stackReducer,
});
const mapActionToProps = (dispatch) => {
return {
home_actions: bindActionCreators(home.actions, dispatch)
}
};
const mapStateToProps = (state) => {
debugger;
return state
};
//connect redux and navigation
const StoreApp = connect(mapStateToProps, mapActionToProps)(RouterAppWithState);
const store = applyMiddleware(thunk)(createStore)(reducer);
export default class RootApp extends Component{
render() {
return(
<Provider store={store}>
<StoreApp />
</Provider>
)
}
}
my home.js ,just import actions and reducers, and export the module
import actions from './store/actions';
import reducer from './store/reducer'
export default {
actions,
reducer
}
my reducers
export default(state=states, action) => {
let newState = {...state};
switch (action.type){
case TYPES.ADD_COUNT: newState.count = state.count+1;break;
default: break;
}
return newState
};
my actions
const add_count = () => {
console.log('add');
return async (dispatch) => {
dispatch({type: TYPES.ADD_COUNT});
await new Promise((resolve) => {setTimeout(() => {resolve()} , 3000)});
dispatch({type: TYPES.ADD_COUNT});
};
};
export default {
add_count
}
my view Home.js
import React, {Component, StyleSheet} from 'react';
import {View, Text, Button} from 'react-native';
export default class Home extends Component{
constructor(props){
super(props)
}
// static contextTypes = {
// navigation: React.PropTypes.Object
// };
render() {
debugger
const {add_count} = this.props.home_actions;
const {count} = this.props.home;
return (
<View>
<Text >{count}</Text>
<Button onPress={add_count} title={'add count'}/>
{/*<Button onPress={() => {navigation.navigate('live')}} title={'live'}/>*/}
</View>
)
}
}
In Home.js function render, this.props is
{
navigation: Object,
screenProps: undefined
}
has not states in redux. but without react-navigation, this.props is
{
home: Object,
home_actions: Object
}
thanks
The reason is because now the StackRouter is controlling the rendering of your Home page. The router only pass down a prop called navigation and whatever you pass down from screenProps.
There are two ways that you can achieve a similar behavior as before:
1) In RouterAppWithState's render function, pass down this.props into screenProps like this
<StackRouter navigation={addNavigationHelpers({
dispatch: this.props.dispatch,
state: this.props.router,
})}
screenProps={this.props}
/>
2) (recommended) The other way is to directly use connect on your home page to connect your Home component to the store and access home specific part of the store. i.e. connect(mapStateToProps)(Home)
I have been following this tutorial to integrate redux into my react native app.
https://github.com/jlebensold/peckish
On my Home view, I'm not able to call the functions from my action folder.
One difference is that I'm using react-navigation in my app. Wonder if I need to integrate redux with react navigation to be able to use redux for all data?
Below is the full implementation code I have been doing.
On the Home screen, I call the fetchSite function on ComponentDidMount to launch an async call with axios. But I can't even access to this function.
Sorry for this long post but I can't figure out how to make this work so quite difficult to make a shorter code sample to explain the structure of my app.
Let me know if any question.
index.ios.js
import React from 'react'
import { AppRegistry } from 'react-native'
import { Provider } from 'react-redux'
import { createStore, applyMiddleware, compose} from 'redux'
import thunkMiddleware from 'redux-thunk'
import { createLogger } from 'redux-logger'
import reducer from './app/reducers'
import AppContainer from './app/index'
// middleware that logs actions
const loggerMiddleware = createLogger({ predicate: (getState, action) => __DEV__ });
function configureStore(initialState) {
const enhancer = compose(
applyMiddleware(
thunkMiddleware, // lets us dispatch() functions
loggerMiddleware,
),
);
return createStore(reducer, initialState, enhancer);
}
const store = configureStore({});
const App = () => (
<Provider store={store}>
<AppContainer />
</Provider>
);
AppRegistry.registerComponent('Appero', () => App;
reducers/index.js
import { combineReducers } from 'redux';
import * as sitesReducer from './sites'
export default combineReducers(Object.assign(
sitesReducer,
));
reducers/sites.js
import createReducer from '../lib/createReducer'
import * as types from '../actions/types'
export const searchedSites = createReducer({}, {
[types.SET_SEARCHED_SITES](state, action) {
let newState = {};
action.sites.forEach( (site) => {
let id = site.id;
newState[id] = Object.assign({}, site, { id });
});
return newState;
},
});
../lib/createReducer
export default function createReducer(initialState, handlers) {
return function reducer(state = initialState, action) {
if (handlers.hasOwnProperty(action.type)) {
return handlers[action.type](state, action)
} else {
return state
}
}
}
../actions/types
export const SET_SEARCHED_SITES = 'SET_SEARCHED_SITES';
AppContainer in ./app/index
import React, { Component } from 'react';
import { connect } from 'react-redux';
import { bindActionCreators } from 'redux';
import { ActionCreators } from './actions';
console.log(ActionCreators); //Properly gathered the functions from the actions folder
import { Root } from './config/router';
window.store = require('react-native-simple-store');
window.axios = require('axios');
class App extends Component {
render() {
return (
<Root />
)
}
}
function mapDispatchToProps(dispatch) {
return bindActionCreators(ActionCreators, dispatch);
}
export default connect(mapDispatchToProps)(App);
ActionCreators in './actions';
import * as SiteActions from './sites'
export const ActionCreators = Object.assign({},
SiteActions,
);
Actions in './actions/sites'
import * as types from './types' //See above
export function fetchSites(token) {
return (dispatch, getState) => {
let instance = axios.create({
baseURL: url + 'api/',
timeout: 10000,
headers: {'Accept' : 'application/json', 'Authorization' : 'Bearer ' + token}
});
instance.get('/sites?page=1')
.then(response => {
console.log(response.data.data);
dispatch(setSearchedSites({sites: response.data.data}));
}).catch(error => {
console.log(error);
});
}
}
export function setSearchedSites({ sites }) {
return {
type: types.SET_SEARCHED_SITES,
sites,
}
}
Root file for navigation based on react-navigation
I made it as simple as possible for this example.
import React from 'react';
import {StackNavigator} from 'react-navigation';
import Home from '../screens/Home';
export const Root = StackNavigator({
Home: {
screen: Home,
}
});
And finally my Home screen
import React, {Component} from 'react';
import { connect } from 'react-redux';
import {Text, View} from 'react-native';
class Home extends Component {
componentDidMount()
{
let token = "12345678" //Just for this example
this.props.fetchSites(token).then( (response) => {
console.log(response);
});
}
render() {
return (
<View>
<Text>This is the Home view</text>
</View>
);
}
}
function mapStateToProps(state) {
return {
searchedSites: state.searchedSites
};
}
export default connect(mapStateToProps)(Home);
To use action methods you need to connect in home screen like this
import { fetchSites } from '<your-path>'
// your Home's other code.
const mapDispatchToProps = (dispatch) => {
return{
fetchSites:dispatch(fetchSites())
}
}
export default connect(mapStateToProps,mapDispatchToProps)(Home);
after that you can use fetchSites as this.props.fetchSites whenever you want.