How to use StackNavigator with Redux? - javascript

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,
}
}

Related

× TypeError: Cannot read properties of undefined (reading 'getState')

I am a beginner learning react and redux. I wrote this demo about how to use connect.js in redux. Searching this kind of question but there is no right answer for my code. I got a undefined context. Is it typo? or I passed context in a wrong way? Thanks in advance. Here is my code.
index.js
import React from "react";
import ReactDOM from "react-dom";
import store from "./store";
import { Provider } from "react-redux";
import App from "./App";
ReactDOM.render(
<Provider store={store}>
<App />
</Provider>,
document.getElementById("root")
);
/store/index.js
import { createStore } from "redux";
import reducer from "./reducer.js";
const store = createStore(reducer);
export default store;
/store/reducer.js
import { ADD, SUB, MUL, DIV } from './constants.js'
// or initialState
const defaultState = {
counter: 0
}
function reducer(state = defaultState, action) {
switch (action.type) {
case ADD:
return {...state, counter: state.counter + action.num};
case SUB:
return {...state, counter: state.counter - action.num};
case MUL:
return {...state, counter: state.counter * action.num};
case DIV:
return {...state, counter: state.counter / action.num};
default:
return state;
}
}
export default reducer
connect.js
import React, { PureComponent } from "react";
import { StoreContext } from "./context";
export default function connect(mapStateToProps, mapDispatchToProps) {
return function enhanceHOC(WrappedCpn) {
class EnhanceCpn extends PureComponent {
constructor(props, context) {
super(props, context);
console.log('connect props', props);
console.log('connect context', context); // context is undefined here
this.state = {
storeState: mapStateToProps(context.getState()),
};
}
componentDidMount() {
this.unSubscribe = this.context.subscribe(() => {
this.setState({
counter: mapStateToProps(this.context.getState()),
});
});
}
componentWillUnmount() {
this.unSubscribe();
}
render() {
return (
<WrappedCpn
{...this.props}
{...mapStateToProps(this.context.getState())}
{...mapDispatchToProps(this.context.dispatch)}
/>
);
}
}
EnhanceCpn.contextType = StoreContext;
return EnhanceCpn;
};
}
context.js
import React from "react";
const StoreContext = React.createContext();
export {
StoreContext
}
App.js
import React, { PureComponent } from 'react'
import My from './pages/my'
export default class App extends PureComponent {
constructor(props, context) {
super(props, context);
console.log('APP props', props);
console.log('APP context', context); // context got value
}
render() {
return (
<div>
<My />
</div>
)
}
}
my.js
import React, { PureComponent } from 'react'
import { sub, mul } from '../store/actionCreators'
import connect from '../utils/connect'
class My extends PureComponent {
render() {
return (
<div>
<h3>my</h3>
<h3>counter: { this.props.counter }</h3>
<button onClick={e => this.props.subNum()}>-2</button>
<button onClick={e => this.props.mulNUm(5)}>*5</button>
</div>
)
}
}
const mapStateToProps = state => ({
counter: state.counter
})
const mapDispatchToProps = dispatch => ({
subNum: (num = -2) => {
dispatch(sub(num))
},
mulNUm: num => {
dispatch(mul(num))
}
})
export default connect(mapStateToProps, mapDispatchToProps)(My)
actionCreators.js
import { ADD, SUB, MUL, DIV } from './constants.js'
export function add(num) {
return {
type: ADD,
num
}
}
export const sub = (num) => {
return {
type: SUB,
num
}
}
export const mul = (num) => ({
type: MUL,
num
})
export const div = num => ({
type: DIV,
num
})
constants.js
const ADD = 'ADD_ACTION'
const SUB = 'SUB_ACTION'
const MUL = 'MUL_ACTION'
const DIV = 'DIV_ACTION'
export { ADD, SUB, MUL, DIV }
From the docs, here is what it says with regards to Class.contextType:
The contextType property on a class can be assigned a Context object
created by React.createContext(). Using this property lets you
consume the nearest current value of that Context type using
this.context. You can reference this in any of the lifecycle methods
including the render function.
It seems that in your case, you are just missing passing your custom StoreContext to redux Provider with the context props
You need to do something like:
import React from "react";
import ReactDOM from "react-dom";
import store from "./store";
import { StoreContext } from "./context";
import { Provider } from "react-redux";
import App from "./App";
ReactDOM.render(
<Provider store={store} context={StoreContext}>
<App />
</Provider>,
document.getElementById("root")
);
See also https://react-redux.js.org/using-react-redux/accessing-store#providing-custom-context
I got the same issue and solved by creating store on the same file as the root component where Provider is applied. Example code below:
<Provider store={createStore(reducers)}>
<App />
</Provider>
Cannot read properties of undefined (reading 'getState')
Solution:
(1) make a file store.js in your redux folder and you can copy the code
import { createStore, applyMiddleware } from "redux";
import logger from 'redux-logger';
import rootReducer from "./root-reducer";
const middlewares = [logger];
const store = createStore(rootReducer, applyMiddleware(...middlewares));
export default store;
(2) then just import the file in index.js file
<Provider store={store}>
<BrowserRouter>
<App />
</BrowserRouter>
</Provider>
i got same issues
i just have to change were i have my configure store from this
import { configureStore } from "#reduxjs/toolkit";
import basketReducer from "../slices/basketSlice";
export const store = configureStore({
reducer: {
basket: basketReducer,
},
});
to this
import { configureStore } from "#reduxjs/toolkit";
import basketReducer from "../slices/basketSlice";
export default configureStore({
reducer: {
basket: basketReducer,
},
});
and this method works for me
i also find out when i play around with the code to understand why it happen and see if i broke it what will happen this is my finding and observation from it
note concerning redux
if you used the below as import in your _app.js
import { store } from "../stores/store";
then
the global store should be rewritten like this
export const store = configureStore({
reducer: {
basket: basketReducer,
},
});
but if you import it like this with out distructuring it[store] or without the curly bracket
import store from "../stores/store";
then
you should write the store like this
export default configureStore({
reducer: {
basket: basketReducer,
},
});
Both ways works for me

Redux - Dispatching Action (onClick Event)

I am simply trying to connect() my LoginPage (component) to my Redux Store and dispatch in action via a onClick (event). When I console.log(this.props) my dispatch handler login() isn't in the component's props.
GitHub Repo -- https://github.com/jdavis-software/demo.git
Question: Why isn't my Redux Store either connection or dispatching the actions?
LoginPage:
import React, { Component} from 'react';
import { connect } from 'react-redux';
export class LoginPage extends Component<any> {
render(){
console.log('props doesnt have contain - login(): ', this.props)
return (<button onClick={ () => '' }>Login</button>)
}
}
const mapProps = state => ({ user: state.user })
const dispatchProps = (dispatch) => {
return {
login: () => dispatch({ type: 'USER_LOGGED_IN', payload: true})
}
}
export default connect(mapProps,dispatchProps)(LoginPage)
Redux Configuration:
import { IStore, IUser } from '#interfaces';
import { createStore, combineReducers } from 'redux';
import ReduxPromise from 'redux-promise';
// reducers
import userReducer from './user.reducer';
// define the intial global store state
const initialState:IStore = {
user: {
isAuthenticated: false
}
}
const appReducer = combineReducers({user: userReducer})
export default createStore(appReducer,initialState);
User Reducer:
// initial state
const initalState:IUser = {
isAuthenticated: false
}
// reducer
const userReducer = (state:IUser = initalState, { type, payload}: IPayload): IUser => {
console.log('user reducer start', state)
switch (type) {
case 'USER_LOGGED_IN':
state = { ...state, isAuthenticated: payload }
break;
default:
return state;
}
return state;
};
export default userReducer;
Root Page:
import React from 'react';
import { render } from 'react-dom';
import { Provider } from 'react-redux';
// styles
import './index.scss';
// pages
import { App } from '#pages';
// store
import store from './core/store/store';
render(
<Provider store={store}>
<App/>
</Provider>, document.getElementById('app')
);
I checked your code on git repository. I found out that you're exporting the named export
export class LoginPage
and the default export,
export default connect(mapProps,dispatchProps)(LoginPage)
But when you're accessing it, you're accessing it as
import { /*Other components*/ , LoginPage } from '#pages'
So it is actually taking the named exported component which is not connected to store.
I suggest you to import as
import LoginPage , { /*Other components*/ } from '#pages'
This might solve your problem.
Return statements are missing in the properties of connect.
const mapProps = state => { return {user: state.user} }
const dispatchProps = (dispatch) => {
return {
login: () => dispatch({ type: 'USER_LOGGED_IN', payload: true})
}
}
export default connect(mapProps,dispatchProps)(LoginPage)
Updated:
Please check Redux-dispatch
try:
import React, { Component} from 'react';
import { connect } from 'react-redux';
export class LoginPage extends Component<any> {
render(){
console.log('props doesnt contain - login(): ', this.props)
return (
<button onClick={ this.props.login }>Login</button>
)
}
}
const mapProps = state => ({ user: state.user })
const dispatchProps = (dispatch) => ({
login: () => dispatch({ type: 'USER_LOGGED_IN', payload: true})
})
export default connect(mapProps,dispatchProps)(LoginPage)
to return an object with Arrow Functions you need to wrap your {} with ()

How do i access redux state from another react component?

I am developing a lottery statistics app that gets data from a csv loaded from an input then I was wanting to read this data to the redux store so I can use it across multiple components.
I have successfully saved the data to the redux store once I import the file and read it through Header.js and using an action, but I am not sure how to access this in other components like e.g. Main.js.
I feel like I am still confused on how react/redux all fits together. I'm sorry if this has been asked before but everything I looked up online I couldn't get to work.
// index.js
import React from "react";
import ReactDOM from "react-dom";
import { createStore, applyMiddleware } from "redux";
import { Provider } from "react-redux";
import thunk from "redux-thunk";
import reducers from "./reducers";
import App from "./components/App";
const store = createStore(reducers, applyMiddleware(thunk));
ReactDOM.render(
<Provider store={store}>
<App />
</Provider>,
document.querySelector("#root")
);
// App.js
import React from "react";
import Header from "./Header";
import Main from "./Main";
const App = () => {
return (
<div>
<Header />
<Main />
<div className="numbers-for-draw"></div>
</div>
);
};
export default App;
// Header.js
import React from "react";
import { CSVReader } from "react-papaparse";
import { fetchData } from "../actions";
import { connect } from "react-redux";
class Header extends React.Component {
constructor(props) {
super(props);
this.fileInput = React.createRef();
}
handleReadCSV = data => {
this.props.fetchData(data);
console.log(this.props.data);
};
handleOnError = (err, file, inputElem, reason) => {
console.log(err);
};
handleImportOffer = () => {
this.fileInput.current.click();
console.log("Got to handleImportOffer");
};
render() {
return (
<header>
<CSVReader
onFileLoaded={this.handleReadCSV}
inputRef={this.fileInput}
style={{ display: "none" }}
onError={this.handleOnError}
/>
<button onClick={this.handleImportOffer}>Import</button>
</header>
);
}
}
//Map what is in the redux store (e.g. state) to props
const mapStateToProps = state => ({
data: state.data
});
export default connect(mapStateToProps, {
fetchData: fetchData
})(Header);
// Main.js
import React from "react";
import { fetchData } from "../actions";
import { connect } from "react-redux";
const Main = () => {
console.log("In main");
console.log(this.props.data); //Blows up here.
return <div>Main</div>;
};
//Map what is in the redux store (e.g. state) to props
const mapStateToProps = state => ({
data: state.data
});
export default connect(mapStateToProps, {
fetchData: fetchData
})(Main);
// actions/index.js
export const fetchData = data => dispatch => {
console.log("Action");
const lottoData = {
stringNumbers: [
"one",
"two",
"three",
...
],
allResults: [],
winningNumbers: [],
winningNumbersAsStrings: []
};
const localData = data.data;
localData.shift();
localData.forEach(line => {
const lineObject = {
draw: line[0],
drawDate: line[1],
ballOne: line[2],
ballTwo: line[3],
ballThree: line[4],
ballFour: line[5],
ballFive: line[6],
ballSix: line[7],
bonusBall: line[8],
bonusBall2: line[9],
powerBall: line[10]
};
lottoData.allResults.push(lineObject);
let nums = [];
nums.push(parseInt(line[2]));
nums.push(parseInt(line[3]));
nums.push(parseInt(line[4]));
nums.push(parseInt(line[5]));
nums.push(parseInt(line[6]));
nums.push(parseInt(line[7]));
nums.sort((a, b) => {
if (a < b) {
return -1;
} else if (a > b) {
return 1;
} else {
return 0;
}
});
lottoData.winningNumbers.push(nums);
lottoData.winningNumbersAsStrings.push(nums.toString());
});
dispatch({ type: "FETCH_DATA", payload: lottoData });
};
// lottoReducer.js
export default (state = {}, action) => {
switch (action.type) {
case "FETCH_DATA":
return action.payload;
default:
return state;
}
};
// reducers/index.js
import { combineReducers } from "redux";
import lottoReducer from "./lottoReducer";
export default combineReducers({
data: lottoReducer
});
I haven't tested your code, but it seems to me that the only problem is in your Main.js
While you use a function component and not a class, you shouldn't use this to access your props. The following should work as expected:
const Main = (props) => {
console.log("In main");
console.log(props.data);
return <div>Main</div>;
};
//Map what is in the redux store (e.g. state) to props
const mapStateToProps = state => ({
data: state.data
});
export default connect(mapStateToProps, {
fetchData: fetchData
})(Main);
In your main.js you used functional components so this.props doesn't work there. You must pass props to your component and console.log(props.data).

How to fix TypeError: undefined is not an object (evaluating 'state.routes')

I'm following the react navigation documentation, react-navigation-redux-helpers's documentation, but always throw me this error. also if i erase the persistGate the error gets fixed but i need to persist some data so that shouldn't be an option
This is my store.js
import { createStore, applyMiddleware } from 'redux';
import { persistStore, persistReducer } from 'redux-persist';
import { AsyncStorage } from 'react-native';
import { createReactNavigationReduxMiddleware } from 'react-navigation-redux-helpers';
import Reducer from './reducers/index';
const persistConfig = {
key: 'root',
storage: AsyncStorage,
blackList: [],
};
const AppReducer = persistReducer(persistConfig, Reducer);
const middleware = createReactNavigationReduxMiddleware(
(state) => state.navigation,
);
export const store = createStore(AppReducer, applyMiddleware(middleware));
export const persistor = persistStore(store);
This is my app-with-state.js
import AppNavigator from './AppNavigator';
const AppNavigatorWithState = createReduxContainer(AppNavigator);
class ReduxNavigation extends React.Component {
render() {
const { state, dispatch } = this.props;
return <AppNavigatorWithState navigation={state} dispatch={dispatch} />;
}
}
const mapStateToProps = (state) => ({
state: state.navigation,
});
export default connect(mapStateToProps)(ReduxNavigation);
this is my AppNavigator.js
const Main = createStackNavigator(
{
Home: Home,
Movie: Movie,
Category: Category,
},
{
defaultNavigationOptions: {
header: Header,
},
},
);
const TabNavigator = createBottomTabNavigator(
{
Home: {
screen: Main,
navigationOptions: {
tabBarIcon: <Icon icon='🏠' />,
},
},
About: { screen: About },
Lucky: { screen: Lucky },
Profile: { screen: Profile },
},
{
tabBarOptions: {
activeTintColor: 'white',
activeBackgroundColor: '#65a721',
},
},
);
//const App = createAppContainer(TabNavigator);
export default TabNavigator;
and here is my App.js
import React from 'react';
import { Provider } from 'react-redux';
import { PersistGate } from 'redux-persist/integration/react';
import { store, persistor } from './store';
import Loader from './src/sections/components/loader';
import ReduxNavigation from './src/app-navigator-with-state';
import { createAppContainer } from 'react-navigation';
const Navigation = createAppContainer(ReduxNavigation);
type Props = {};
export default class App extends React.Component<Props> {
render() {
return (
<Provider store={store}>
<PersistGate persistor={persistor} loading={<Loader />}>
<Navigation />
</PersistGate>
</Provider>
);
}
}
I know its too late but it should help others :))
In your mystore.js
const middleware = createReactNavigationReduxMiddleware(
(state) => state.navigation,
)
should be look like this:
const middleware = createReactNavigationReduxMiddleware(
(state) => state.router,
)
I also got this error and I didn't find any clue, and stuck. After several hours of debugging, it was coz of my stupid mistake.
This was my Wrong Case:
const customContent = ({props}) => { }
Acutally, props doesn't need {}. Just call (props) like and error was fixed.
This is Right Case:
const customContent = (props) => { }
Please Don't add {} when you call props.
Hope it helps for someone who got error like me.
I also got this error and it was because i accidentally called:
navigation.pop("nameOfScreen");
I forgot to change "pop" when I changed from pop(1) to navigate("")
Maybe this will help someone else who is as tired as I am rn.

ReferenceError: ReferenceError: Can't find variable: dispatch

I'm trying to use redux with react-native. I have created a fetch post data example app , then I just wanted to use with mapDispatchToProps method. I read the documentation and look at some tutorial, it looks similar.
Problem is when I try to use mapDispatchToProps its return error:
ReferenceError: ReferenceError: Can't find variable: dispatch
An error appears in HomeScreen
My Home Screen
import React from 'react';
import { View } from 'react-native';
import { connect } from 'react-redux';
import { getPosts } from '../actions/index';
class HomeScreen extends React.Component {
state = {
data:[],
}
componentDidMount() {
this.props.getPosts()
}
render() {
return (
<View style={styles.container}>
</View>
);
}
}
function mapStateToProps (state) {
return {
posts: state.posts
};
};
function mapDispatchToProps (dispatch) {
return {
getPosts: () => {
dispatch(getPosts())
}
};
};
export default connect(mapStateToProps, mapDispatchToProps)(HomeScreen)
My Store
import { createStore } from 'redux';
import rootReducer from '../reducers';
export default store = createStore(rootReducer);
My Action
import { GET_PGET_POST_LOADING,GET_POST_RECEIVED,GET_POST_ERROR } from './actionTypes';
import { GET_POST_URL } from '../api';
export const getPosts = () => {
dispatch({
type: GET_POST_LOADING
});
fetch(GET_POST_URL).then((data) => {
console.log(data);
dispatch({
type:GET_POST_RECEIVED,
payload:data
})
}).catch((error) => {
console.log(error)
})
}
My Reducer
import { GET_POST_LOADING, GET_POST_RECEIVED, GET_POST_ERROR } from '../actions/actionTypes';
const Post = (state = { posts: [] , loading: true }, action) => {
console.log(action);
switch(action.type) {
case GET_POST_LOADING:
return {
...state,
loading: true
}
case GET_POST_RECEIVED:
return {
loading:false,
posts: action.payload
}
case GET_POST_ERROR:
return state;
default: return state;
}
}
export default Post;
My Appjs
import React from 'react';
import { Platform, StatusBar, StyleSheet, View } from 'react-native';
import { AppLoading, Asset, Font, Icon } from 'expo';
import AppNavigator from './navigation/AppNavigator';
import { Provider } from 'react-redux';
import store from './store/index';
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" />}
<AppNavigator />
</View>
</Provider>
);
}
}
I'm new about Redux, where I'm missing?
Thanks in advance :)

Categories

Resources