How can I access the field values in a parent component of a redux-form component?
I'm not sure if it's caused by typescript, but before I started using typescript, I was able to access the form values through mapStateToProps just like how I have it currently. I've been trying to figure out what was different to my previous implementation but the only difference would be the versions of the npm dependencies and the addition of typescript.
LoginPage.tsx
import LoginForm from 'components/Forms/LoginForm'
import Layout from 'components/Layout'
import { StatusCodes } from 'lib/enums/statusCodes'
import { storeAuthToken } from 'lib/helpers/auth'
import { NextPage } from 'next'
import Router from 'next/router'
import React from 'react'
import { connect, DispatchProp } from 'react-redux'
import { FormInstance } from 'redux-form'
interface IProps {
login: FormInstance<IFormData, IFormProps>
}
interface IState {
errorMessage?: string,
processing: boolean
}
interface IRootState {
form: IProps
}
export interface IFormData {
username?: string,
password?: string
}
export interface IFormProps {
contactId?: string,
errorMessage?: string,
fieldValues: Partial<IFormData>,
processing: boolean
}
class LoginPage extends React.Component<NextPage & DispatchProp & IProps, IState> {
state = {
errorMessage: undefined,
processing: false
}
setErrorMessage = (message: string) => {
this.setState({
errorMessage: message,
processing: false
})
}
handleSubmit = async (values: IFormData) => {
if (values && values.username && values.password) {
this.setState({
errorMessage: undefined,
processing: true
})
try {
const { dispatch } = this.props
await storeAuthToken(dispatch, values.username, values.password)
Router.push('/')
} catch (error) {
if (error === StatusCodes.BAD_REQUEST) {
this.setErrorMessage("Sorry, you have entered incorrect details. Please try again.")
} else {
this.setErrorMessage("Sorry, there was an issue trying to log you in")
}
}
}
}
render() {
const { login } = this.props
const { processing } = this.state
return (
<Layout title="Login">
<div className="form-wrapper full">
<LoginForm processing={processing} onSubmit={this.handleSubmit} fieldValues={login.values} />
</div>
</Layout>
)
}
}
const mapStateToProps = ({ form: { login } }: IRootState) => ({ login })
export default connect(mapStateToProps)(LoginPage)
LoginForm.tsx
import Link from 'next/link'
import React from 'react'
import { Field, InjectedFormProps, reduxForm } from 'redux-form'
import FormButton from 'components/Forms/FormButton'
import Input from 'components/Fields/Input'
import { validateRequired } from 'lib/helpers/validators'
import { IFormProps, IFormData } from 'pages/login'
class LoginForm extends React.Component<IFormProps & InjectedFormProps<IFormData, IFormProps>> {
render() {
const { contactId, errorMessage, fieldValues, handleSubmit, processing } = this.props
return (
<form id="login" onSubmit={handleSubmit} >
<h1>Sign in</h1>
<fieldset>
<div className="fields">
{
!contactId
? <Field name="username" type="text" component={Input} label="Username" validate={validateRequired} />
: <Field name="username" type="email" component={Input} label="Email" validate={validateRequired} />
}
</div>
<div className="fields">
<Field name="password" type="password" component={Input} label="Password" validate={validateRequired} />
</div>
</fieldset>
{ errorMessage && <p className="error-message">{errorMessage}</p> }
<div className="form-bottom">
<Link href="/"/*{`/forgot-password${fields.email ? `?email=${encodeURIComponent(fields.email)}` : ''}`}*/>
<a className="inline">Forgotten your password?</a>
</Link>
<FormButton loading={processing}>
Login
</FormButton>
</div>
</form>
)
}
}
export default reduxForm<{}, IFormProps>({ form: 'login' })(LoginForm)
Here is my redux store file incase if that is coded incorrectly
import { createWrapper, HYDRATE, MakeStore } from 'next-redux-wrapper'
import { AnyAction, applyMiddleware, combineReducers, createStore, Reducer } from 'redux'
import { reducer as formReducer } from 'redux-form'
import thunkMiddleware, { ThunkMiddleware } from 'redux-thunk'
import authReducer, { AuthState } from './auth/reducer'
import contactReducer, { ContactState } from './contact/reducer'
import initialState from './initialState'
export interface State {
auth: AuthState
contact: ContactState
}
const bindMiddleware = (middleware: [ThunkMiddleware]) => {
if (process.env.NODE_ENV !== 'production') {
const { composeWithDevTools } = require('redux-devtools-extension')
return composeWithDevTools(applyMiddleware(...middleware))
}
return applyMiddleware(...middleware)
}
const combinedReducer = combineReducers({
auth: authReducer,
contact: contactReducer,
form: formReducer
})
const reducer: Reducer = (state: State, action: AnyAction) => {
if (action.type === HYDRATE) {
const nextState: Reducer = {
...state,
...action.payload
}
return nextState
} else {
return combinedReducer
}
}
const makeStore: MakeStore<State> = () => createStore(reducer, initialState, bindMiddleware([thunkMiddleware]))
export const wrapper = createWrapper<State>(makeStore/*, { debug: true }*/)
It seems like I missed out a key in the IApplicationState interface and as mentioned by #cbr, the parameters state and action needed to be passed to combinedReducer even though it doesn't directly take any.
Additionally it didn't like when the nextState constant had the type Reducer, so I have changed that to CombinedState<State> as well
The changed code looks like this
import { createWrapper, HYDRATE, MakeStore } from 'next-redux-wrapper'
import { AnyAction, applyMiddleware, combineReducers, createStore, Reducer } from 'redux'
import { reducer as formReducer } from 'redux-form'
import thunkMiddleware, { ThunkMiddleware } from 'redux-thunk'
import authReducer, { AuthState } from './auth/reducer'
import contactReducer, { ContactState } from './contact/reducer'
import initialState from './initialState'
export interface State {
auth: AuthState
contact: ContactState,
form: FormStateMap
}
const bindMiddleware = (middleware: [ThunkMiddleware]) => {
if (process.env.NODE_ENV !== 'production') {
const { composeWithDevTools } = require('redux-devtools-extension')
return composeWithDevTools(applyMiddleware(...middleware))
}
return applyMiddleware(...middleware)
}
const combinedReducer = combineReducers({
auth: authReducer,
contact: contactReducer,
form: formReducer
})
const reducer: Reducer = (state: State, action: AnyAction) => {
if (action.type === HYDRATE) {
const nextState: CombinedState<State> = {
...state,
...action.payload
}
return nextState
} else {
return combinedReducer(state, action)
}
}
const makeStore: MakeStore<State> = () => createStore(reducer, initialState, bindMiddleware([thunkMiddleware]))
export const wrapper = createWrapper<State>(makeStore)
Related
I have spent so much time on this, still can't tell what's going on, I'm working on a react app to send post request to an api, each time i click the add button to dispatch action, I get an error "Action must be plain object"
Here is actions/index.js
import { createAction } from "redux-actions";
import * as actions from "../actionTypes
export const CreateUserRequest = createAction(actions.CREATE_USER_REQUEST);
export const CreateUserSuccess = createAction(actions.CREATE_USER_SUCCESS);
export const CreateUserError = createAction(actions.CREATE_USER_ERROR);
actionTypes/index.js
export const CREATE_USER_REQUEST = "CREATE_USER_REQUEST";
export const CREATE_USER_SUCCESS = "CREATE_USER_SUCCESS";
export const CREATE_USER_ERROR = "CREATE_USER_ERROR";
reducers/createUser.js
import * as actions from "../actionTypes";
const initialState = {
isLoading: false,
isSuccess: false,
isError: false
};
const createUser = (state = initialState, action) => {
switch (action.type) {
case actions.CREATE_USER_REQUEST:
return {
...state,
isLoading: true,
isSuccess: false,
isError: false
};
case actions.CREATE_USER_SUCCESS:
return {
...state,
isLoading: false,
isSuccess: true,
isError: false,
result: action.payload,
};
case actions.CREATE_USER_ERROR:
return {
...state,
isLoading: false,
isSuccess: false,
isError: true,
result: action.payload,
};
default:
return state;
}
};
export default createUser;
reducers/index.js
import { combineReducers } from "redux";
import CreateUserReducer from './createUser';
const rootReducer = combineReducers({
CreateUserStatus:CreateUserReducer,
//logout:logout,
})
export default rootReducer
saga/createUser.js
import { call, put, takeLatest } from "redux-saga/effects";
import { CreateUserSuccess, CreateUserError, CreateUserRequest } from '../actions/index';
import axiosCall from "../../services";
import * as actions from "../actionTypes"
export function* createUserRequest(action) {
try {
const response = yield call(
axiosCall,
"POST",
'/api/createUser',
action.payload,
);
if (response) {
yield put(CreateUserSuccess({ response : response.data}));
} else {
yield put(CreateUserError({ error: "Invalid details" }));
}
} catch (error) {
console.log('errrrrrrrrr::',error)
yield put(CreateUserError({ error: "Invalid detailssssssss" }));
}
yield takeLatest(actions.CREATE__USER_REQUEST, CreateUserRequest);
}
saga/index.js
import { fork, all } from "redux-saga/effects";
import {createUserRequest} from './createUser';
function* rootSaga() {
{
yield all([
fork(createUserRequest),
]);
}
}
export default rootSaga;
store.js
import { createStore, applyMiddleware } from "redux";
import createSagaMiddleware from "redux-saga";
import rootReducer from "../reducers";
import rootSaga from "../saga/index";
import { createLogger } from "redux-logger";
const middleWares = [];
import { composeWithDevTools } from "redux-devtools-extension";
const logger = createLogger({
predicate: () => process.env.NODE_ENV === 'development',
collapsed: true
});
const sagaMiddleware = createSagaMiddleware();
middleWares.push(sagaMiddleware);
middleWares.push(logger);
const store = createStore(rootReducer, composeWithDevTools(applyMiddleware(...middleWares)));
sagaMiddleware.run(rootSaga);
export default store;
Component createUser.js
import React, { useState} from 'react';
import { useDispatch } from "react-redux"
import {
Container,
} from '../../components';
import { createUserRequest } from '../../redux/actions';
const createUser = () => {
const dispatch = useDispatch()
const [newuser,setNewuser] = useState({
first_name : 'Jon',
last_name : 'Doe',
email : 'jondoe#gmail.com',
phone_number: '+91234455',
status:0
})
const handleChange = (e) =>{
setNewuser({...newuser,[e.target.name] : e.target.value});
}
//Add new user
const add_user = ()=>{
dispatch(createUserRequest(newuser));
}
return (
<Container>
<form>
<input type="text" className="form-control" name="first_name" onChange={handleChange}/>
<input type="text" className="form-control" name="last_name" onChange={handleChange}/>
<input type="text" className="form-control" name="email" onChange={handleChange}/>
<input type="text" className="form-control" name="phone_number" onChange={handleChange}/>
<input type="button" className="form-control" onClick={()=>{add_user()}}/>
</form>
</Container>
)
}
export default createUsers;
Error message I am getting now
Uncaught TypeError: Object(...) is not a function
at add_user (createusers.js?2170:55)
at onClick (createusers.js?2170:82)
at HTMLUnknownElement.callCallback (react-dom.development.js?61bb:188)
at Object.invokeGuardedCallbackDev (react-dom.development.js?61bb:237)
at invokeGuardedCallback (react-dom.development.js?61bb:292)
at invokeGuardedCallbackAndCatchFirstError (react-dom.development.js?61bb:306)
at executeDispatch (react-dom.development.js?61bb:389)
at executeDispatchesInOrder (react-dom.development.js?61bb:414)
at executeDispatchesAndRelease (react-dom.development.js?61bb:3278)
at executeDispatchesAndReleaseTopLevel (react-dom.development.js?61bb:3287)
It points out to this line
dispatch(createUserRequest(newuser));
Issue
It appears you are attempting to dispatch your saga function, createUserRequest.
import { createUserRequest } from '../../redux/saga/createUser';
...
//Add new user
const add_user = () => {
dispatch(createUserRequest({ newuser: newuser }));
}
Solution
You need to create an action that accepts a "new user" payload. Perhaps you meant to use CreateUserRequest.
import { createAction } from "redux-actions";
import * as actions from "../actionTypes;
export const CreateUserRequest = createAction(actions.CREATE_USER_REQUEST); // <-- ***
export const CreateUserSuccess = createAction(actions.CREATE_USER_SUCCESS);
export const CreateUserError = createAction(actions.CREATE_USER_ERROR);
So in the createUser component.
import React, { useState } from 'react';
import { useDispatch } from "react-redux";
import { Container } from '../../components';
import { CreateUserRequest } from '../../redux/actions';
const createUser = () => {
const dispatch = useDispatch()
const [newuser, setNewuser] = useState({
first_name : '',
last_name : '',
email : '',
phone_number: '',
status:0
});
...
//Add new user
const add_user = () => {
dispatch(CreateUserRequest({ newuser })); // <-- dispatch action payload
}
return (
...
)
}
I am trying to combine navigation, more exactly stack navigation with my react native redux application, and I encountered this error during debugging. From what I've already searched, it's because the navigation isn't really compatible with the redux mode of work. So I tried a solution to my problem, but still I have the same error. Here is my code:
Login.js
import React, { Component } from 'react';
import { View, ActivityIndicator, TouchableHighlight } from 'react-native';
import { getLogger, issueToText } from '../core/utils';
import styles from '../core/styles';
import { Card, Button, FormLabel, FormInput } from "react-native-elements";
import { connect } from 'react-redux'
import { loginAction } from '../actions/LoginActions'
class LoginComponent extends Component {
constructor(props) {
super(props);
this.login = this.login.bind(this)
}
render() {
const { error, isLoading } = this.props;
const inputFormProp = {
username: '',
password: ''
};
return (
<View style={{ paddingVertical: 20 }}>
<Card>
<FormLabel>Email</FormLabel>
<FormInput value={inputFormProp.username} onChangeText={(text) => inputFormProp.username = text} />
<FormLabel>Password</FormLabel>
<FormInput value={inputFormProp.password} onChangeText={(text) => inputFormProp.password = text} />
<Button
buttonStyle={{ marginTop: 20 }}
backgroundColor="#03A9F4"
title="SIGN IN"
onPress={this.login(inputFormProp)}
/>
</Card>
<ActivityIndicator animating={this.props.isLoading} style={styles.activityIndicator} size="large" />
</View>
);
}
login(inputFormProp) {
console.log(this.props)
const { store } = this.props.screenProps.store;
console.log(loginAction)
const { dispatch } = this.props
console.log(dispatch)
dispatch(loginAction(inputFormProp))
.then(() => {
if (this.props.error === null && this.props.isLoading === false) {
if (store.getState().auth.token) {
this.props.navigation.navigate('ProductList', { token: store.getState().auth.token });
}
}
})
.catch(error => {
});
}
}
function mapStateToProps(state) {
const { error, isLoading } = state.auth
return {
error,
isLoading,
}
}
export default connect(mapStateToProps)(LoginComponent)
App.js
import React, { Component } from 'react';
import { createStore, applyMiddleware, combineReducers, compose } from 'redux';
import { createLogger } from 'redux-logger';
import thunk from 'redux-thunk';
import { authReducer } from "./src/reducers/LoginReducer";
import { productReducer } from "./src/product/service";
import { ProductList } from "./src/product/ProductList";
import { LoginComponent } from "./src/components/Login";
import { Provider, connect } from "react-redux";
import { StackNavigator, addNavigationHelpers } from "react-navigation";
import Routes from "./src/core/routes";
const AppNavigator = StackNavigator(Routes, {
navigationOptions: {
title: ({ state }) => {
if (state.params) {
return `${state.params.title}`;
}
}
}
});
const navReducer = (state, action) => {
const newState = AppNavigator.router.getStateForAction(action, state);
return newState || state;
};
#connect(state => ({
nav: state.nav
}))
class AppWithNavigationState extends Component {
render() {
return (
<AppNavigator
navigation={addNavigationHelpers({
dispatch: this.props.dispatch,
state: this.props.nav
})}
/>
);
}
}
const initialState = {
auth: { isLoading: false, error: null },
};
const rootReducer = combineReducers({ product: productReducer, auth: authReducer, nav: navReducer });
let store = createStore(rootReducer, initialState,
compose(applyMiddleware(thunk, createLogger())));
export default function App() {
return (
<Provider store={store}>
<AppWithNavigationState />
</Provider>
);
}
Routes.js
import { ProductList } from "../product/ProductList";
import { LoginComponent } from "../components/Login";
const Routes = {
Login: { screen: LoginComponent },
ProductList: { screen: ProductList }
};
export default Routes;
Here is my error: Route Login should declare a screen.
What did I do wrong with my code? Thank you.
I fixed the error. It was because I added between LoginComponent {} in the routes.js file at:
import { LoginComponent } from "../components/Login";
After one of the Redux tutorials decided to implement that facny action->reducer->store->view chain for simple app with only login part.
Seems like all setted up but when I run my app - in mapStateToProps(currentState) no any sign of the any custom state fields which I expected to see! (default state from reducer). But the action function is fine, as you can see on the screenshot
I can't see whats wrong here so, decided to ask it.
So here is the code
So, first of all - store.js
import { createStore, applyMiddleware } from 'redux';
import rootReducer from '../reducers';
import thunk from 'redux-thunk';
export default function configureStore(initialState) {
const store = createStore(rootReducer, initialState, applyMiddleware(thunk));
if (module.hot) {
module.hot.accept('../reducers', () => {
const nextRootReducer = require('../reducers');
store.replaceReducer(nextRootReducer);
});
}
return store;
}
then login reducer
const initialState = {
user: {
name: '',
password: ''
},
fetching: false
}
export default function login(state = initialState, action) {
switch (action.type) {
case LOGIN_REQUEST: {
return { ...state, fetching: true }
}
case LOGIN_SUCCESS: {
return { ...state, user: action.data, fetching: false }
}
case LOGIN_FAIL: {
return { ...state, user: -1, fetching: false }
}
default: {
return state;
}
}
}
and the root (reducers/index.js):
import login from './login/login';
import { combineReducers } from 'redux'
export default combineReducers({
login
});
the action
import {
LOGIN_REQUEST,
LOGIN_SUCCESS,
LOGIN_FAIL
} from '../../constants/login.js';
export function onLoginAttempt(userData) {
return (dispatch) => {
dispatch({
type: LOGIN_REQUEST,
user: userData
})
tryLogin(userData);
}
};
function tryLogin(userData) {
let url = 'SignIn/Login ';
return (dispatch) => {
axios.post(url, userData)
.then((response) => dispatch({
type: LOGIN_SUCCESS,
data: response.data
})).error((response) => dispatch({
type: LOGIN_FAIL,
error: response.error
}))
}
};
So here is entrance point:
import React from 'react';
import ReactDOM from 'react-dom';
import { createStore } from 'redux';
import { Provider } from 'react-redux';
import App from './containers/app.js';
import configureStore from './store/configureStore';
let store = createStore(configureStore);
ReactDOM.render(
<Provider store={store}>
<App />
</Provider>,
document.getElementById("content")
);
and here is the app.js (Login is just sompe custom div with two fields nothing special)
import React, { Component } from 'react';
import { bindActionCreators } from 'redux'
import { connect } from 'react-redux';
import Login from '../components/login/Login';
import * as pageActions from '../actions/login/login'
class App extends Component {
render() {
const { user, fetching } = this.props;
const { onLoginAttempt } = this.props.pageActions;
return <div>
<Login name={user.name} password={user.password} fetching={fetching} onLoginAttempt={onLoginAttempt} />
</div>
}
}
function mapStateToProps(currentState) {
return {
user: currentState.user,
fetching: currentState.fetching
}
}
function mapDispatchToProps(dispatch) {
return {
pageActions: bindActionCreators(pageActions, dispatch)
}
}
export default connect(mapStateToProps, mapDispatchToProps)(App)
I see that you have state which looks like:
reduxState = {
login: {
user: {
name: '',
password: ''
},
fetching: false
}
}
but then you try to access properties that don't exist.
function mapStateToProps(currentState) {
return {
user: currentState.user,
fetching: currentState.fetching
}
}
I think you need to:
function mapStateToProps(currentState) {
return {
user: currentState.login.user,
fetching: currentState.login.fetching
}
}
You have this line let store = createStore(configureStore); on your entry point. However, inside configureStore, you have a call to createStore()
Basically you're calling something like createStore(createStore(reducers)). That's probably the cause of the problem.
You should probably call it like
let store = configureStore( /* pass the initial state */ )
I try to add more props to existing list i am able to fire dispatch action but reducer doesn't get trigger
i have my headercontainer.js file like
import React, { Component, PropTypes } from 'react'
import { connect } from 'react-redux'
import { fetchAccount } from 'actions/account'
import { getAccount } from 'reducers/account'
import { fetchCart } from 'actions/cart'
import { getCart } from 'reducers/cart'
//import * as cls from 'actions/cls'
import { getReadmore } from 'reducers/cls'
import * as cls from 'actions/cls'
import { fetchAuthenticityToken } from 'actions/authenticityToken'
import { getAuthenticityToken } from 'reducers/authenticityToken'
import Header from 'components/Header'
class HeaderContainer extends Component {
static propTypes = {
account: PropTypes.object,
authenticityToken: PropTypes.object,
cart: PropTypes.object,
dispatch: PropTypes.func
}
componentDidMount() {
if (typeof document !== 'undefined') {
if (!this.props.account.isFetching) {
this.props.dispatch(fetchAccount())
}
if (!this.props.authenticityToken.isFetching) {
this.props.dispatch(fetchAuthenticityToken())
}
if (!this.props.cart.isFetching) {
this.props.dispatch(fetchCart())
}
}
}
constructor(props) {
super(props);
this.state = {classToSend: true };
}
stateToRender(){
(this.state.classToSend) ? this.setState({classToSend: false}) : this.setState({classToSend: true});
}
onClickHandler(){
this.stateToRender();
let action = cls.readMore(this.state.classToSend)
this.props.dispatch(action)
// this.props.readMore(this.state.classToSend);
}
render() {
const { account, cart, authenticityToken } = this.props
if(!account || !cart) {
return false
}
return (
<div id ="cvb">
<div id="toggle-nav" className={this.state.toggleClass?'visible-xs nav-open':'visible-xs'} onClick={() => this.onClickHandler()} >
<span data-action="toggle-nav" className="action mt-menu-label">
<span className="mt-menu-bread mt-menu-bread-top">
<span className="mt-menu-bread-crust mt-menu-bread-crust-top"></span>
</span>
<span className="mt-menu-bread mt-menu-bread-middle">
<span className="mt-menu-bread-crust mt-menu-bread-crust-middle"></span>
</span>
<span className="mt-menu-bread mt-menu-bread-bottom">
<span className="mt-menu-bread-crust mt-menu-bread-crust-bottom"></span>
</span>
</span>
</div>
<Header account={account} cart={cart} />
</div>
)
}
}
const mapStateToProps = (state) => {
return {
account: getAccount(state),
cart: getCart(state),
classToSend: getReadmore(state),
authenticityToken: getAuthenticityToken(state)
}
}
export default connect(mapStateToProps)(HeaderContainer)
My cls.js reducer
export function getReadmore(state) {
console.log(state.readMore1)
console.log("are yar")
return state.readMore1
}
export function readMore1 (state="", action) {
console.log(action.type)
switch(action.type){
case READ_MORE:
return action.payload;
}
return state;
}
cls.js Action
export function readMore(class_something) {
const READ_MORE = 'READ_MORE';
console.log("--------------------------")
console.log(class_something)
return {
type: READ_MORE,
payload: class_something
};
}
Though i am able to call action cls.js file but reducer not getting trigger
can anyone help me to get out of this trouble.
My edits
my index.js in reducer folder
import { combineReducers } from 'redux'
import { reducer as form } from 'redux-form'
import { routerReducer } from 'react-router-redux'
import { currency } from './currency'
import { cart } from './cart'
import { account } from './account'
import { alerts } from './alerts'
import { login } from './login'
import { authenticityToken } from './authenticityToken'
import { products } from './products'
import { product } from './product'
const routing = routerReducer
const rootReducer = combineReducers({
form,
routing,
currency,
cart,
cls,
account,
alerts,
authenticityToken,
login,
products,
product
})
export default rootReducer
Your case in your reducer file should be "READ_MORE" (with quotations) not READ_MORE (without quotations).
I have been trying to learn to This is a simple login, logout app using Redux. On pressing the Login Button from Display.js, the login_action function should be called. But an error is showing with title Cannot read property login_action of undefined. I tried logging the props in Display.js and I am able to see the functions in the logs but somehow the functions aren't being called. What is it that I'm missing or unable to find out?
Basic App :
/* App.js */
import React, {Component} from 'react';
import { createStore, applyMiddleware, combineReducers } from 'redux';
import { Provider } from 'react-redux';
import thunk from 'redux-thunk';
import * as reducers from './reducers';
import SceneContainer from './containers/SceneContainer';
const createStoreWithMiddleware = applyMiddleware(thunk)(createStore);
const reducer = combineReducers(reducers);
const store = createStoreWithMiddleware(reducer);
export default class App extends Component {
render() {
return (
<Provider store={store}>
<SceneContainer />
</Provider>
);
}
}
Container:
/* containers/SceneContainer.js */
'use strict';
import React, {Component, PropTypes} from 'react';
import Display from '../components/display';
import * as loginActions from '../actions/';
import {connect} from 'react-redux';
import {bindActionCreators} from 'redux';
class SceneContainer extends Component {
constructor(props) {
super(props);
}
render() {
const {actions} = this.props;
console.log(actions);
return (
<Display {...actions}/>
);
}
}
SceneContainer.propTypes = {
user: PropTypes.object.isRequired,
actions: PropTypes.object.isRequired
};
function mapStateToProps(state) {
return {user: state.auth.user};
}
function mapDispatchToProps(dispatch) {
console.log(loginActions);
return {
actions: bindActionCreators(loginActions, dispatch)
};
}
export default connect(mapStateToProps, mapDispatchToProps)(SceneContainer);
Component :
/* components/display.js */
import React, {Component, PropTypes} from 'react';
import {
View,
Text,
StyleSheet,
TouchableHighlight,
} from 'react-native';
class Display extends Component {
constructor(props) {
super(props);
console.log(props.login_action);
}
onLoginPress() {
this.props.login_action({
username: 'ranat',
password: 'password'
});
}
onLogoutPress() {
this.props.logout_action();
}
render() {
return (
<View>
<TouchableHighlight onPress={this.onLoginPress}>
<Text>Login</Text>
</TouchableHighlight>
<TouchableHighlight onPress={this.onLogoutPress}>
<Text>Logout</Text>
</TouchableHighlight>
</View>
);
}
}
Display.propTypes = {
logout_action: PropTypes.func.isRequired,
login_action: PropTypes.func.isRequired
};
export default Display;
Actions file :
/* actions/index.js */
import {LOGIN_ACTION, LOGOUT_ACTION, LOGIN_SUCCESS, LOGIN_FAILURE} from './actionTypes';
export var login_action = (userCredentials) => {
if(userCredentials.username === 'ranat' && userCredentials.password === 'password') {
return {
type: LOGIN_ACTION,
value: LOGIN_SUCCESS,
};
}
else {
return {
type: LOGIN_ACTION,
value: LOGIN_FAILURE,
};
}
};
export var logout_action = () => {
return {
type: LOGOUT_ACTION,
}
};
Reducers :
/* reducers/login.js */
import {LOGIN_ACTION, LOGOUT_ACTION, LOGIN_SUCCESS, LOGIN_FAILURE} from '../actions/actionTypes'
let cloneObject = (obj) => {
if(obj)
return JSON.parse(JSON.stringify(obj));
else
return {};
}
const initialState = {
user: {
loggedIn: false,
},
};
const auth = (state = initialState, action = {}) => {
switch(action.type) {
case LOGIN_ACTION: {
if(action.value === LOGIN_SUCCESS) {
return {
...state,
user: {
loggedIn: true
}
};
}else {
return {
...state,
user: {
loggedIn: false
}
};
}
}
case LOGOUT_ACTION: {
if(action.value === LOGIN_SUCCESS) {
return {
...state,
user: {
loggedIn: false
}
};
}else {
return state;
}
}
default: {
return state || initialState;
}
}
}
export default auth;
/* reducers/index.js */
import { combineReducers } from 'redux';
import auth from './login';
export {
auth,
};
Change onPress={this.onLoginPress} to onPress={this.onLoginPress.bind(this}.
Do the same for onPress={this.onLogoutPress} also.