I'm trying to implement redux to show balance in multiple screens as I update balance in single screen it should reflect in all other screens/components.
I'm pretty new to redux. As you know with complexity around redux, its even making difficult to implement it.
I followed some examples in GitHub and youtube and started implementing it .
Under Actions folder I have. following two files
counteractions.js
import * as types from './actionTypes.js';
//ActionCreator methods
export function updateBalance(balanceInfo) {
return {
type: types.LEDGER_BALANCE,
payLoad: { balanceInfo }
}
}
Under Reducers folder.I have this file
balance.js
import * as types from '../actions/actionTypes.js';
const initialState = {
balance: 0
}
// reducer functions .. accepts current/initial state , actions and returns new state
const balanceReducer=(state,action)=>
{
switch (action.type) {
case types.LEDGER_BALANCE:
return {
balance: action.payload.balanceInfo
}
break;
default:
break;
}
}
export default balanceReducer;
in ConfigureStore.js
import {createStore} from 'redux';
import rootReducer from './reducers/index.js';
import balanceReducer from './reducers/balance.js';
const initailState = {
balance: 0,
}
export const store=createStore(balanceReducer,balanceReducer);
App.js
/**
* Sample React Native App
* https://github.com/facebook/react-native
* #flow
*/
import React, { Component } from 'react';
import { Provider } from 'react-redux';
//Provider - makes redux store available to connect() class in component hierarchy below
import { applyMiddleware, createStore, compose, combineReducers } from "redux";
import thunkMiddleware from 'redux-thunk';
import createLogger from 'redux-logger';
import rootReducer from './reducers/index.js';
//import store from './configureStore.js';
import {
Platform,
StyleSheet,
Text,
View,
TouchableOpacity,
TextInput
} from 'react-native';
import ReduxDemo from "./reduxDemo.js";
import { store, reducer } from './balanceDemo.js';
const instructions = Platform.select({
ios: 'Press Cmd+R to reload,\n' +
'Cmd+D or shake for dev menu',
android: 'Double tap R on your keyboard to reload,\n' +
'Shake or press menu button for dev menu',
});
export default class App extends Component<{}> {
constructor(props) {
super(props);
this.state = {
balancelocal: '',
}
}
_updateLedger = () => {
// store.dispatch({ type: 'BALANCE', payLoad: '500' });
store.dispatch({ type: 'BALANCE', payLoad: 'Your balance is 8000 MUR' });
}
render() {
store.subscribe(() => {
this.setState({
balancelocal: store.getState(),
})
//this.balanceInfo = store.getState().balance;
// alert(this.state.balancelocal);
});
return (
<View style={styles.container}>
<TouchableOpacity onPress={this._updateLedger}>
<Text>Update balance</Text>
</TouchableOpacity>
<TextInput style={{height:100,width:400}} value={this.state.balancelocal}/>
</View>
);
}
}
const styles = StyleSheet.create({
container: {
flex: 1,
justifyContent: 'center',
alignItems: 'center',
backgroundColor: '#F5FCFF',
},
welcome: {
fontSize: 20,
textAlign: 'center',
margin: 10,
},
instructions: {
textAlign: 'center',
color: '#333333',
marginBottom: 5,
},
});
I'm yet to complete configure store file. and. I'm wondering. where I have to subscribe and dispatch actions ..
I want to update balance with button click from app.js
I have. to update balance in another page automatically..
Please guide me to understand and implement redux .Please suggest better folder structure and better way to implement redux.
There is quite a bit here to understand.
The basic workflow is (you can have the receiving component as a different one)
Component Button > Action > Reducer > Component Props > Render
To achieve this you need both the setup of the store and the invoking of the "event" through redux.
Below is an example (excuse if not perfect, just typed into here), but the way the other component gets the value from the action is becuase it uses the 'connect' HOC. Everytime redux gets a state change it calls all components that are 'connected'. Here we take the updated balance and return it as part of the 'mapStateToProps' function, which is just setting the components props with that object. The balance is then accessed via this.props.balance and displayed.
This becomes more useful if you want to either call an api in the action and store the result in the reducer. All connected components will then get that new data.
Note1: I have only used redux-thunk middleware to dispatch so forgive me for using that.
Note2: This is a simple example. When the app gets better you will need to prevent over-rendering since any reducer changes will invoke all connected components. I use reselect here.
Setup
reducers.js
import { combineReducers } from 'redux';
import { balanceReducer } from 'balanceReducer';
export default combineReducers({
balanceReducer
})
store.js
import { createStore, applyMiddleware } from 'redux';
import thunk from 'redux-thunk'
import combineReducers from './reducers'
export default function configureStore() {
let store = createStore(combineReducers, applyMiddleware(thunk));
return store;
}
index.js
import React, { Component } from 'react';
import { AppRegistry, View } from 'react-native';
import { Provider } from 'react-redux';
import configureStore from './store';
import Component1 from './component1';
const store = configureStore()
const myapp = () => (
<Provider store={store}>
<View>
<Component1 />
<View>
</Provider>
)
AppRegistry.registerComponent('myapp', () => myapp);
Components
component1.js (key part is the connect HOC)
import { connect } from 'react-redux';
import React, { Component } from 'react';
import { Text, View, TouchableOpacity } from 'react-native';
import { updateBalance } from './action';
class Component1 extends Component {
_updateLedger = () => {
this.props.updateBalance(500);
}
render() {
const { balance } = this.props;
return (
<View>
<TouchableOpacity onPress={this._updateLedger}>
<Text>Update balance</Text>
</TouchableOpacity>
<Text>{balance}</Text>
</View>
)
}
}
function mapStateToProps(state) {
return {
balance: state.balanceReducer.balance
}
}
function mapDispatchToProps(dispatch) {
return {
updateBalance = (balanceInfo) => dispatch(updateBalance(balanceInfo))
};
}
export default connect(
mapStatetoProps,
mapDispatchToProps
)(Component1)
action.js
export function updateBalance(balanceInfo) {
return {
type: types.LEDGER_BALANCE,
payLoad: { balanceInfo }
}
}
balanceReducer.js (key part here is to return new state)
const initialState = {
balance: 0,
}
export function balanceReducer(state = initialState, action) {
if(action.type === types.LEDGER_BALANCE) {
return {
balance: action.payLoad.balanceInfo
}
}
}
Related
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
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
This is probably something very basic. There is a spinner on my App where the routes and providers are declared. This must be reading the redux store, in particular spinner.visible and map to state so I can hide/show the <Spinner> element.
But as I said...this is the entry file of the app. I know how to map it to props using connect, but looks like I can't use connect/mapStateToProps on my entry file.
This works very good, but I don't think that using a subscribe is the best way. I'd like to make the spinner be capable to read the store directly in an elegant way. Any suggestions ?
import React from 'react'
import {Provider} from 'react-redux'
import {View} from 'react-native'
import {createStore, applyMiddleware} from 'redux'
import Spinner from 'react-native-loading-spinner-overlay'
import ReduxThunk from 'redux-thunk'
import reducers from './reducers'
import Routes from './config/routes'
import {getReady} from './services/registration'
import {setAppInitialLoad, setAppSpinner} from './actions/AppActions'
class App extends React.Component {
constructor(props) {
super(props)
this.state = {
initialized: false,
spinner: {
visible: false,
text: ''
}
}
store.subscribe(() => {
//ToDo: I really hope to find an elegant solition for this.
//Since it' the top level JS file of the app, I can't use
//connect/mapStateToProps to map the props :(
const spinner = store.getState().AppReducer.spinner
if(spinner.visible != this.state.spinner.visible) {
this.setState({
spinner: {
visible: spinner.visible,
text: spinner.text
}
});
}
}
)
}
componentDidMount() {
store.dispatch(setAppSpinner({ visible: true, text: 'Loading...'}))
getReady().then(response => {
store.dispatch(setAppInitialLoad(response.data.data))
store.dispatch(setAppSpinner({ visible: false, text: ''}))
this.setState({initialized: true})
})
}
render() {
if (this.state.initialized) {
return (
<View>
<Provider store={store}>
<Routes/>
</Provider>
<Spinner visible={this.state.spinner.visible} textContent={this.state.spinner.text}
textStyle={{color: '#000'}}/>
</View>
)
} else {
return (
<View style={{backgroundColor: 'yellow', flex: 1}}/>
)
}
}
}
const store = createStore(reducers, {}, applyMiddleware(ReduxThunk))
export default App;
Use can use store variable
(In your code, it here: const store = createStore(reducers, {}, ...)
store variable has some method, you can read at here (https://redux.js.org/basics/store)
I'm trying to implement redux to show balance in multiple screens as I update balance in single screen it should reflect in all other screens/components.
I'm pretty new to redux. As you know with complexity around redux, its even making difficult to implement it.
I followed some examples in GitHub and youtube and started implementing it .
under Actions folder I have. following two files
counteractions.js
import * as types from './actionTypes.js';
//ActionCreator methods
export function updateBalance(balanceInfo) {
return {
type: types.LEDGER_BALANCE,
payLoad: { balanceInfo }
}
}
Under Reducers folder.I have this file
balance.js
import * as types from '../actions/actionTypes.js';
const initialState = {
balance: 0
}
// reducer functions .. accepts current/initial state , actions and returns new state
const balanceReducer=(state,action)=>
{
switch (action.type) {
case types.LEDGER_BALANCE:
return {
balance: action.payload.balanceInfo
}
break;
default:
break;
}
}
export default balanceReducer;
in ConfigureStore.js
import {createStore} from 'redux';
import rootReducer from './reducers/index.js';
import balanceReducer from './reducers/balance.js';
const initailState = {
balance: 0,
}
export const store=createStore(balanceReducer,balanceReducer);
App.js
/**
* Sample React Native App
* https://github.com/facebook/react-native
* #flow
*/
import React, { Component } from 'react';
import { Provider } from 'react-redux';
//Provider - makes redux store available to connect() class in component hierarchy below
import { applyMiddleware, createStore, compose, combineReducers } from "redux";
import thunkMiddleware from 'redux-thunk';
import createLogger from 'redux-logger';
import rootReducer from './reducers/index.js';
//import store from './configureStore.js';
import {
Platform,
StyleSheet,
Text,
View,
TouchableOpacity,
TextInput
} from 'react-native';
import ReduxDemo from "./reduxDemo.js";
import { store, reducer } from './balanceDemo.js';
const instructions = Platform.select({
ios: 'Press Cmd+R to reload,\n' +
'Cmd+D or shake for dev menu',
android: 'Double tap R on your keyboard to reload,\n' +
'Shake or press menu button for dev menu',
});
export default class App extends Component<{}> {
constructor(props) {
super(props);
this.state = {
balancelocal: '',
}
}
_updateLedger = () => {
// store.dispatch({ type: 'BALANCE', payLoad: '500' });
store.dispatch({ type: 'BALANCE', payLoad: 'Your balance is 8000 MUR' });
}
render() {
store.subscribe(() => {
this.setState({
balancelocal: store.getState(),
})
//this.balanceInfo = store.getState().balance;
// alert(this.state.balancelocal);
});
return (
<View style={styles.container}>
<TouchableOpacity onPress={this._updateLedger}>
<Text>Update balance</Text>
</TouchableOpacity>
<TextInput style={{height:100,width:400}} value={this.state.balancelocal}/>
</View>
);
}
}
The styling for it
const styles = StyleSheet.create({
container: {
flex: 1,
justifyContent: 'center',
alignItems: 'center',
backgroundColor: '#F5FCFF',
},
welcome: {
fontSize: 20,
textAlign: 'center',
margin: 10,
},
instructions: {
textAlign: 'center',
color: '#333333',
marginBottom: 5,
},
});
I'm yet to complete configure store file. and. I'm wondering, where I have to subscribe and dispatch actions ...
I want to update balance with button click from app.js
I have. to update balance in another page automatically..
Please guide me to understand and implement redux .Please suggest better folder structure and better way to implement redux.
https://redux.js.org/basics/exampletodolist
The above link has a basic setting up of react with redux. It is almost similar for React Native too.
You need to wrap your App component inside Provider imported from 'react-redux', and then give that to AppRegistry. Also you do not seem to have imported any actions and haven't used the connect function either. Like the comment above, it is better for you go through a video guide on basics of redux. It'll help you understand all the complexity, and once you understand, nothing is as easy as redux. All the best.
I'm getting the error:
Actions may not have an undefined "type" property.
But I'm sure I defined it and spelled it right.
App:
import React, {Component} from 'react';
import { createStore, applyMiddleware, combineReducers } from 'redux';
import { Provider } from 'react-redux';
import { AsyncStorage } from 'react-native';
import thunk from 'redux-thunk';
import {persistStore, autoRehydrate} from 'redux-persist';
import FBLoginView from '../components/FBLoginView'
import * as reducers from '../reducers';
import Routing from './Routing';
const createStoreWithMiddleware = applyMiddleware(thunk)(createStore);
const reducer = combineReducers(reducers);
const store = createStoreWithMiddleware(reducer, undefined, autoRehydrate());
persistStore(store, {
storage: AsyncStorage,
}, () => {
})
export default class App extends Component {
render() {
return (
<Provider store={store}>
<Routing />
</Provider>
);
}
}
Actions:
import * as types from './actionTypes';
export function getFacebookUser(user) {
return {
type: types.GET_FACEBOOK_USER,
user: user,
};
}
Types:
export const GET_FACEBOOK_USER = 'GET_FACEBOOK_USER';
Reducer:
import * as types from '../actions/actionTypes';
const initialState = {
user: {},
};
export default function reducer(state = initialState, action = {}) {
switch (action.type) {
case types.GET_FACEBOOK_USER:
return {
...state,
user: action.user
};
default:
return state;
}
}
Edit (My home.js page)
import React, { Component } from 'react'
import { View, Text, StyleSheet, Image, TouchableHighlight } from 'react-native'
import { Actions } from 'react-native-router-flux'
import {FBLogin, FBLoginManager} from 'react-native-facebook-login'
import FBLoginView from '../components/FBLoginView'
import * as facebookActions from '../actions/facebookActions';
import { connect } from 'react-redux'
import {bindActionCreators} from 'redux'
class Home extends Component {
constructor(props) {
super(props);
this.state = {
login: false
};
console.log(this.props)
}
render() {
let { facebook, actions } = this.props
_onLogin = (e) => {
actions.getFacebookUser(e.profile)
console.log(facebook)
}
_onLogout = (e) => {
console.log(e)
}
return (
<View style={styles.background}>
<Text>{this.state.login ? "Logged in" : "Logged out"}</Text>
<FBLogin
buttonView={<FBLoginView />}
ref={(fbLogin) => { this.fbLogin = fbLogin }}
loginBehavior={FBLoginManager.LoginBehaviors.Native}
permissions={["email","user_friends"]}
onLogin={function(e){_onLogin(e)}}
onLoginFound={function (e){console.log(e)}}
onLoginNotFound={function(e){console.log(e)}}
onLogout={function(e){_onLogin(e)}}
onCancel={function(e){console.log(e)}}
onError={function(e){console.log(e)}}
onPermissionsMissing={function(e){console.log(e)}}
style={styles.fbButton}
passProps={true}
/>
</View>
)
}
}
export default connect(store => ({
facebook: store.facebook.user,
}),
(dispatch) => ({
actions: bindActionCreators(facebookActions, dispatch)
})
)(Home);
const styles = StyleSheet.create({
background: {
flex: 1,
flexDirection: 'column',
justifyContent: 'center',
alignItems: 'center',
backgroundColor: '#00796B',
},
});
I don't think that you're dispatching the action correctly:
actions.getFacebookUser(e.profile)
is an action creator and will just return the action, not dispatch it.
I can't see your Home component that you're hooking up with Connect but I'd guess this is the source of events that you will want to dispatch as actions. Why not try dispatching directly against the store, and then move to use connect to hook up with mapDispatchToProps? Finally you can use bindActionCreators if this is necessary.
There are two very good (free) egghead.io courses that will help here, both by Dan Abramov:
https://egghead.io/courses/getting-started-with-redux
https://egghead.io/courses/building-react-applications-with-idiomatic-redux
and the docs are also very good, but I guess you've seen them.
After seeing more of the code, I can't see how the component you're connecting (Home) is linking its events (for example onLogin) to a dispatch property. I can see it caling its own internal function called _onLogin, but this just in turn call the action creator, it won't dispatch.
The connect function allows you connect properties on a component (here, Home) with the redux store; it effectively links, in your example, the 'onLogin' property of your Home component with a particular action and can then dispatch that action to the store.
So,your Home component needs to accept a property like 'onLogin' that it can then call; mapDispatchToProps is a function you write to marry up your child component's properties to dispatch actions. bindActionCreators is just a further helper to bind to action creators; it may be overkill in your current use case.
Dan Abramov explains this so much better than I can, so see the docs, but also see his answer here:
How to get simple dispatch from this.props using connect w/ Redux?