I am trying to set up react redux, also i have set it but I am getting an error TypeError: _this.props.showLoader is not a function. (In '_this.props.showLoader(true)', '_this.props.showLoader' is undefined).
this.props.showLoader(true)
This function has been defined in Action.js file you can find it below.
This error comes whenever I try to call a function from Root.js file, you can find it below.
Below is the code what I have done so far:->
I have App.js file in which I have set for provider and store
import React, { Component } from 'react';
import {StyleSheet, View, Text} from 'react-native';
import {Provider} from 'react-redux';
import store from './src/redux/Store'
import Root from './src/root/Root'
function App() {
return (
<Provider store={store}>
<View style={localStyles.container}>
<Root/>
</View>
</Provider>
);
}
export default App;
const localStyles = StyleSheet.create({
container: {
flex: 1,
},
});
This is the Navigation Route File
import React from 'react'
import { NavigationContainer } from '#react-navigation/native';
import { createStackNavigator } from '#react-navigation/stack';
import TutorialSwipeScreen from '../components/viewcontrollers/onboarding/TutorialSwipeScreen'
const Stack = createStackNavigator();
function goStack() {
return (
<NavigationContainer>
<Stack.Navigator
screenOptions={{
headerShown: false
}}
>
<Stack.Screen name="Login" component={Login Screen} />
</Stack.Navigator>
</NavigationContainer>
);
}
export default goStack;
My Root.js file
where I am trying to this.props.showLoader it gives me error
import React, {Component} from 'react'
import {View, StyleSheet, Text} from 'react-native'
import NavigationRoute from './NavigationRoutes'
import DeviceInfo from 'react-native-device-info'
class Root extends Component {
constructor(props) {
super(props)
console.warn('props=', this.props)
}
componentDidMount() {
this.call()
}
call = () => {
this.props.showLoader(true)
}
render () {
return (
<View style={styles.rootContainer}>
<NavigationRoute/>
</View>
)
}
}
export default Root;
const styles = StyleSheet.create({
rootContainer: {
flex: 1,
}
});
My RootContainer.js File
import {bindActionCreators} from 'redux'
import {connect} from 'react-redux'
import * as Actions from '../redux/Actions'
import Root from './Root'
function mapStateToProps(state) {
return {
shouldShowLoader: state.dataReducer._showLoader,
shouldChangeCounting: state.dataReducer.counter
}
}
function mapDispatchToProps(dispatch) {
const mergedActions = Object.assign({}, Actions);
return bindActionCreators(mergedActions, dispatch)
}
export default connect(mapStateToProps, mapDispatchToProps)(Root);
My Store.js file
import {createStore} from 'redux'
import reducers from './RootReducer'
export default createStore(reducers);
My RootReducer.js
import {combineReducers} from 'redux'
import dataReducer from './reducers/Reducer'
const rootReducer = combineReducers ({
dataReducer,
})
export default rootReducer;
My Action.js file which has a function showLoader(bool) which I try to call from Root.js which gives me an error as I have quoted above.
export const DISPLAY_LOADER = 'DISPLAY_LOADER';
export const REFRESH = 'REFRESH';
export const COUNTER = 'COUNTER';
export function showLoader(bool) {
return {
type: DISPLAY_LOADER, data: bool,
};
}
export function refresh() {
return {
type: REFRESH, data: true,
};
}
export function counting(count) {
return {
type: COUNTER, data: count
}
}
And Finally Reducer.js file code goes here
import { DISPLAY_LOADER, REFRESH, WELCOME_POPUP, LOGIN_RELOAD, MESSAGE_POPUP, LOGOUT, COUNTER} from '../Actions';
const initialState = {
counter: 5,
_showLoader: false,
_showMessagePopup: false,
_loginReload: false,
_refresh: false,
_heading: 'Message Heading',
_message: 'PWG Custom Message',
}
const dataReducer = (state = initialState, action) => {
switch(action.type) {
case DISPLAY_LOADER: {
return {...initialState, _showLoader: action.data}
}
case REFRESH: {
return {...initialState, _refresh: action.data}
}
case LOGOUT: {
return {...initialState, _refresh: true}
}
case COUNTER: {
return {...initialState, counter: action.data}
}
default: {
return state;
}
}
}
export default dataReducer;
So where my mistake is I am not able to find with what lines of code I am receiving error at my end. Please help. Also I am new to react native please bear with me.
Thanks
I have got the answer, I am answering my own post.
In the App.js File You need to import
RootContainer
instead of
Root
That’s it and it works Voila.
import RootContainer from './src/root/RootContainer'
I see that your showLoader tied to your redux reducer.
what i can suggest you, you should return state not initialState in your reducer. Basically what youre doing is after each change of state in redux, everything else will go back to its initialState but not the changed state itself.
You also didn't pass props showLoader to your Root component. If your intention is using showLoader as a setState function for your Root component, you should define the function in your mapDispatchToProps in your connect function
Related
Thank you very much in advance
I have a native reagent application that is in the following order of components:
app.tsx:
import React from 'react';
import { Routes } from './src/routes';
import { AppProvider } from './src/hooks';
export default function App() {
return (
<AppProvider>
<Routes />
</AppProvider>
);
}
I just needed to use the navigation properties inside a hooks:
hook/index.tsx
import React, { ReactNode, useContext } from 'react';
import {
NavigationContainer,
NavigationContext,
} from '#react-navigation/native';
import { AuthProvider } from './auth';
import { CommonProvider } from './common';
interface AppProviderProps {
children: ReactNode;
}
function AppProvider({ children }: AppProviderProps) {
return (
<CommonProvider>
<AuthProvider>{children}</AuthProvider>
</CommonProvider>
</NavigationProvider>
);
}
export { AppProvider };
hook example:
hook/CommonProvider.tsx:
import React, { createContext, ReactNode, useContext, useState } from 'react';
import { Dispatch, SetStateAction } from 'react';
type CommonContextData = {
isLoading: boolean;
setIsLoading: Dispatch<SetStateAction<boolean>>;
};
interface CommonProviderProps {
children: ReactNode;
}
const CommonContext = createContext<CommonContextData>({} as CommonContextData);
function CommonProvider({ children }: CommonProviderProps) {
const [isLoading, setIsLoading] = useState<boolean>(false);
//const {navigate} = useNavigation()//here I could use the navigation methods ???????
return (
<CommonContext.Provider value={{ isLoading, setIsLoading }}>
{children}
</CommonContext.Provider>
);
}
function useCommon(): CommonContextData {
const context = useContext(CommonContext);
return context;
}
export { CommonProvider, useCommon };
how would I do the following implementation?
I believe you need to wrap the Root component with the NavigationContainer. Once done, you can use the useNavigation hook in any child component.
For instance inside the CommonProvider you can use the hook useEffect in that way.
const navigation = useNavigation();
useEffect(()=>{
navigation.navigate('YourNextScreenName')
}, [navigation])
I managed to solve it as follows:
persist a file of
routes/RootNavigation.ts
import { createNavigationContainerRef } from '#react-navigation/native';
export const navigationRef = createNavigationContainerRef();
export function navigate(name: string, params: any) {
if (navigationRef.isReady()) {
navigationRef.navigate(name,params);
}
}
in my case what contains the centralization of routes in the file add the
navigationRef, no NavigationContainer:
routes/index.tsx
...
<NavigationContainer linking={linking} independent ref={navigationRef}>
...
using in file any hook:
...
function handleMovePage() {
// navigation.navigate('SignIn');
RootNavigation.navigate('SelectArea', { userName: 'Lucy' });
}
...
reference:
https://reactnavigation.org/docs/navigation-context/
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.
index.js
import React from 'react';
import {
AppRegistry
} from 'react-native'
import App from './App';
import { YellowBox } from 'react-native';
YellowBox.ignoreWarnings(['Warning: isMounted(...) is deprecated', 'Module RCTImageLoader']);
AppRegistry.registerComponent('mhagora', () => App);
App.js
import React, { Component } from 'react';
import { Provider } from "react-redux";
import store from './app/store';
import { StyleProvider, getTheme } from "native-base";
import Setup from "./app/setup";
import variables from "./app/theme/variables/commonColor";
export default class App extends Component {
render() {
return (
<Provider store={store}>
<StyleProvider style={getTheme(variables)}>
<Setup />
</StyleProvider>
</Provider>
);
}
}
./app/setup.js
import React, { Component } from "react";
import axios from "axios/index";
import Config from "./config";
import { Root } from "native-base";
import AppNavigator from "./routes";
axios.defaults.baseURL = Config.API_BASE_URL;
axios.defaults.headers.common['Content-Type'] = Config.API_ACCEPT;
axios.defaults.headers.common['Accept'] = Config.API_ACCEPT;
axios.defaults.headers.common['secret'] = Config.API_SECRET;
export default class Setup extends Component {
render() {
return (
<Root>
<AppNavigator />
</Root>
);
}
}
./app/store/index.js
import { createStore, applyMiddleware, compose } from 'redux';
import thunk from 'redux-thunk';
import { createLogger } from 'redux-logger';
import reducers from '../reducers';
const logger = createLogger();
export default createStore(reducers, compose(applyMiddleware(thunk, logger)));
./app/actions/index.js
import { APP_LOADING, APP_LOADED } from '../actionTypes';
export function appLoading() {
return (dispatch) => {
dispatch({type: APP_LOADING});
}
}
export function appLoaded() {
return (dispatch) => {
dispatch({type: APP_LOADED});
}
}
./app/actions/user.js
import { USER_LOADING, USER_LOADED, USER_FAILED, APP_LOADING, APP_LOADED } from "../actionTypes";
import axios from 'axios';
import Config from '../config';
export function userLogin(username, password) {
return (dispatch) => {
dispatch({type: USER_LOADING});
axios
.post("oauth/token", {
username: username,
password: password,
client_id: Config.API_CLIENT_ID,
client_secret: Config.API_CLIENT_SECRET,
grant_type: 'password',
}, {
headers: {}
})
.then(response => {
dispatch({
type: USER_LOADED,
data: response.data
});
})
.catch(err => {
dispatch({ type: USER_FAILED, error: err.response.data.message });
alert(err.response.data.message);
});
};
}
./app/reducers/index.js
import appReducer from './appReducer';
import userReducer from './userReducer';
import { combineReducers } from "redux";
const rootReducer = combineReducers({
appReducer,
userReducer
});
export default rootReducer;
./app/reducers/userReducer.js
import { USER_LOADING, USER_LOADED, USER_FAILED } from '../actionTypes';
const initialState = {
username: "",
password: "",
user: {}
};
export default userReducer = (state = initialState, action) => {
switch (action.type) {
case USER_LOADING:
return Object.assign({}, state, {
loading: true,
user: {},
});
case USER_LOADED:
return Object.assign({}, state, {
loading: false,
user: action.data
});
case USER_FAILED:
return Object.assign({}, state, {
loading: false,
});
default:
return state
}
}
./app/reducers/appReducer.js
import { APP_LOADING, APP_LOADED } from "../actionTypes";
const initialState = {
loading: true,
};
export default appReducer = (state = initialState, action) => {
switch (action.type) {
case APP_LOADING:
return Object.assign({}, state, {
loading: true
});
case APP_LOADED:
return Object.assign({}, state, {
loading: false
});
default:
return state;
}
};
./app/screens/home.js
'use strict';
import React, { Component } from 'react';
import { bindActionCreators } from 'redux';
import { connect } from 'react-redux';
import { SkypeIndicator } from 'react-native-indicators';
import * as Actions from '../actions/index';
import { Container, Header, Title, Content, Footer, FooterTab, Button, Left, Right, Body, Icon, Text, View } from 'native-base';
class HomeScreen extends Component {
componentDidMount() {
/** HERE, the apps should show a loading page forever but it didn't **/
// setTimeout( _ => {
// this.props.appLoaded();
// }, 2000);
}
render() {
if (this.props.loading) {
return (
<SkypeIndicator />
);
} else {
return (
<Container>
<Header>
</Header>
<Body>
<Button
onPress={() =>
this.props.navigation.navigate('LoginScreen')
}><Text>Login now</Text></Button>
<Text>Hello</Text>
</Body>
</Container>
);
}
}
}
// The function takes data from the app current state,
// and insert/links it into the props of our component.
// This function makes Redux know that this component needs to be passed a piece of the state
function mapStateToProps(state, props) {
return {
loading: state.loading,
user: state.user,
}
}
// Doing this merges our actions into the component’s props,
// while wrapping them in dispatch() so that they immediately dispatch an Action.
// Just by doing this, we will have access to the actions defined in out actions file (action/homeScreen.js)
function mapDispatchToProps(dispatch) {
return bindActionCreators(Actions, dispatch);
}
//Connect everything
export default connect(mapStateToProps, mapDispatchToProps)(HomeScreen);
./app/screens/loginScreen
'use strict';
import React, { Component } from 'react';
import { StyleSheet } from 'react-native';
import { SkypeIndicator } from 'react-native-indicators';
import { bindActionCreators } from "redux";
import { connect } from "react-redux";
import { Body, Button, Container, Content, Header, Icon, Left, Text, Title, View } from "native-base";
import t from 'tcomb-form-native';
import { LoginUserModel, LoginUserModelOption } from "../models/UserModel";
import styles from '../styles';
import LoadingButton from 'react-native-loading-button';
import * as UserActions from '../actions/user';
const Form = t.form.Form;
const ps = StyleSheet.create({
...styles,
container: {
justifyContent: 'center',
marginTop: 50,
padding: 20
},
});
class LoginScreen extends Component {
constructor(props) {
super(props);
}
onSubmitHandler = () => {
const value = this._form.getValue();
if(value) {
this.props.userLogin(value.username, value.password);
}
};
render() {
return (
<Container>
<Header>
<Left>
<Button transparent onPress={() => this.props.navigation.goBack()}>
<Icon name="arrow-back"/>
</Button>
</Left>
<Body>
<Title>Headers</Title>
</Body>
</Header>
<Content padder>
<View style={ps.container}>
<Form ref={c => this._form = c} type={LoginUserModel} options={LoginUserModelOption} />
<LoadingButton
block
onPress={this.onSubmitHandler.bind(this)}
isLoading={this.props.loading}
style={{ justifyContent: 'center' }}
><Icon name="checkmark"/><Text>Login Now</Text></LoadingButton>
</View>
</Content>
</Container>
);
}
}
// The function takes data from the app current state,
// and insert/links it into the props of our component.
// This function makes Redux know that this component needs to be passed a piece of the state
function mapStateToProps(state, props) {
return {
loading: state.loading,
user: state.user,
}
}
// Doing this merges our actions into the component’s props,
// while wrapping them in dispatch() so that they immediately dispatch an Action.
// Just by doing this, we will have access to the actions defined in out actions file (action/homeScreen.js)
function mapDispatchToProps(dispatch) {
return bindActionCreators(UserActions, dispatch);
}
//Connect everything
export default connect(mapStateToProps, mapDispatchToProps)(LoginScreen);
the homeScreen should result in a forever loading page but it didn't
the loginScreen button should automatically loading when pressing, but it didn't
new to react-native, i have tried to set/play with the state/props but it just seems like not changing/connected, i also have another page trying to check the state is synced but results is like always get the fresh state, as my understanding the state is something like GLOBAL variable accessible in any component connect to redux
MY QUESTION IS
1. is the react-native/redux/redux-thunk setup correctly? if not, where is the error
2. is the state/props is global accessible in any component that connect with redux
3. if statement 2 is correct, what the different between state/props? this.state and this.props
4. i don't really understand the promise work, how can we handle / wait untill the api call complete(success/error) before move to next step/flow, i use php a lot and my logic is stuck at each function should return something then depends on the results process to next function...and then...
your answer / precious time spend for reading this question is appreciated, thank you
created a github for easy to reproduce/test
https://github.com/weiloon1234/react-native-test
I am using Redux with a React Native app, and am getting a red box error when trying to integrate Redux-Persist. As soon as I navigate to the initial screen which has a ProfileForm and has the this.props.age piece of state, I see this:
TypeError: Cannot read property "age" of undefined
I am not quite sure where I am going wrong. I have set initial value of age = 0 in the reducer. So how is it undefined? Here is code that would be relevant:
reducers/UserReducer.js
import { CALCULATE_BMI, CALCULATE_AGE } from '../actions/types';
const INITIAL_STATE = { bmi: 0, age: 0 };
export default (state = INITIAL_STATE, action) => {
switch (action.type) {
case CALCULATE_BMI:
return { ...state, bmi: action.bmi };
case CALCULATE_AGE:
return { ...state, age: action.age };
default:
return state;
}
};
reducers/index.js
import { combineReducers } from 'redux';
import { reducer as formReducer } from 'redux-form';
import userReducer from './UserReducer';
export default combineReducers({
user: userReducer,
form: formReducer,
});
store/index.js
import { createStore, applyMiddleware, compose } from 'redux';
import { persistStore, persistCombineReducers } from 'redux-persist';
import storage from 'redux-persist/es/storage'; // default: localStorage if web, AsyncStorage if react-native
import thunk from 'redux-thunk';
import logger from 'redux-logger';
import reducers from '../reducers';
const config = {
key: 'root',
storage,
};
const reducer = persistCombineReducers(config, { reducers });
const composeEnhancers = window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__ || compose;
export default () => {
const store = createStore(reducer, {}, composeEnhancers(applyMiddleware(thunk, logger)));
const persistor = persistStore(store);
return { persistor, store };
};
ProfileForm.js
import React, { Component } from 'react';
import PropTypes from 'prop-types';
import { StyleSheet, Text, View, TouchableOpacity, Alert } from 'react-
native';
import { compose } from 'redux';
import { connect } from 'react-redux';
import { reduxForm, Field } from 'redux-form';
import * as actions from '../actions';
import DatePickerModal from '../components/DatePickerModal';
class ProfileForm extends Component {
// other code here
<Field name="birthdate" component={DatePickerModal} />
{this.props.age === 0 ? null : (
<Text style={styles.labelStyle}>{`Age: ${this.props.age}`}</Text>
)}
//other code here
}
const mapStateToProps = state => ({
age: state.user.age,
});
export default compose(connect(mapStateToProps, actions), reduxForm({ form: 'Profile', validate }))(
ProfileForm,
);
App.js
import React, { Component } from 'react';
import { Provider } from 'react-redux';
import { PersistGate } from 'redux-persist/es/integration/react';
import MainNavigator from './routes';
import configureStore from './store';
const { persistor, store } = configureStore();
export default class App extends Component {
// other code here
render() {
return (
<Provider store={store}>
<PersistGate persistor={persistor}>
<MainNavigator />
</PersistGate>
</Provider>
);
}
}
I have tried removing the empty object {} passed to createStore, setting it as undefined, and setting the initial values here instead, but nothing seems to work. Any guidance appreciated.
I'm playing with react-native / redux and am dispatching an action that is supposed to display a number yet an error gets thrown:
Unhandled JS Exception: Objects are not valid as a React child (found:
object with keys {type, payload}). If you meant to render a collection
of children, use an array instead or wrap the object using
createFragment(object) from the React add-ons. Check the render method
of Text.
createStore.js
import { createStore, applyMiddleware, combineReducers } from 'redux';
import createLogger from 'redux-logger';
import numReducer from './reducers/numReducer';
const logger = createLogger();
export default (initialState = {}) => (
createStore(
combineReducers({
numbers: numReducer
}),
initialState,
applyMiddleware(logger)
)
);
App.js
import React from 'react';
import { Provider } from 'react-redux';
import HomeScreen from './components/HomeScreen';
import createStore from './createStore';
const store = createStore();
export default () => (
<Provider store={store}>
<HomeScreen />
</Provider>
);
numReducer.js
import { LIST_NUMBERS, PICK_NUMBER } from '../actions/actionTypes';
export default (state = [], action = {}) => {
switch (action.type) {
case LIST_NUMBERS:
return action.payload || [];
case PICK_NUMBER:
return action.payload;
default:
return state;
}
};
HomeScreen.js
import React from 'react';
import { View } from 'react-native';
import NavContainer from '../containers/NavContainer';
const HomeScreen = () => (
<View>
<NavContainer />
</View>
);
export default HomeScreen;
NavContainer.js
import { bindActionCreators } from 'redux';
import { connect } from 'react-redux';
import { listNumbers, pickNumber } from '../actions/numberActions';
import Nav from '../components/Nav';
const mapStateToProps = state => ({
numbers: state.numbers
});
const mapDispatchToProps = dispatch => (
bindActionCreators({
listNumbers,
pickNumber
}, dispatch)
);
export default connect(
mapStateToProps,
mapDispatchToProps
)(Nav);
Nav.js
import React, { Component, PropTypes } from 'react';
import { View, Text } from 'react-native';
export default class Nav extends Component {
render() {
return (
<View>
<Text>FirstLine</Text>
<Text>SecondLind</Text>
<Text>Number: {this.props.pickNumber(3)}</Text>
</View>
);
}
}
Please advise what I am doing wrong. Thank you
You need to dispatch your action from inside one of your lifecycle methods or on some handler, and then use the (updated) props from your redux store in your component.
Example:
import React, { Component, PropTypes } from 'react';
import { View, Text } from 'react-native';
export default class Nav extends Component {
componentDidMount() {
this.props.pickNumber(3);
}
render() {
return (
<View>
<Text>FirstLine</Text>
<Text>SecondLind</Text>
<Text>Number: {this.props.numbers}</Text>
</View>
);
}
}