"Cannot read property of undefined" being thrown by react-apollo used with React-NextJS - next.js

My NextJS project has been giving me grief over Graph QL these days. I've been trying to implement an Apollo client solution to retrieve data from a remote GraphQL server into a custom component. But no matter which solution I try, I always end up with this error. Here's my current react-apollo implementation:
// /lib/with-apollo-client.js
import React from "react";
import Head from "next/head";
import { getDataFromTree } from "react-apollo";
import initApollo from "./init-apollo";
export default App => {
return class WithData extends React.Component {
static displayName = `WithData(${App.displayName})`;
static async getInitialProps(ctx) {
const { Component, router } = ctx;
const apollo = initApollo({});
ctx.ctx.apolloClient = apollo;
let appProps = {};
if (App.getInitialProps) {
appProps = await App.getInitialProps(ctx);
}
// Run all GraphQL queries in the component tree
// and extract the resulting data
if (!process.browser) {
try {
// Run all GraphQL queries
await getDataFromTree(
<App
{...appProps}
Component={Component}
router={router}
apolloClient={apollo}
/>
);
} catch (error) {
console.error("Error while running `getDataFromTree`", error);
}
// getDataFromTree does not call componentWillUnmount
// head side effect therefore need to be cleared manually
Head.rewind();
}
// Extract query data from the Apollo store
const apolloState = apollo.cache.extract();
return {
...appProps,
apolloState
};
}
constructor(props) {
super(props);
this.apolloClient = initApollo(props.apolloState);
}
render() {
return <App {...this.props} apolloClient={this.apolloClient} />;
}
};
};
// /lib/init-apollo.js
import { ApolloClient, InMemoryCache, HttpLink } from 'apollo-boost'
import fetch from 'isomorphic-unfetch'
let apolloClient = null
// Polyfill fetch() on the server (used by apollo-client)
if (!process.browser) {
global.fetch = fetch
}
function create (initialState) {
// Check out https://github.com/zeit/next.js/pull/4611 if you want to use the AWSAppSyncClient
return new ApolloClient({
connectToDevTools: process.browser,
ssrMode: !process.browser, // Disables forceFetch on the server (so queries are only run once)
link: new HttpLink({
uri: 'https://api.graph.cool/simple/v1/cixmkt2ul01q00122mksg82pn', // Server URL (must be absolute)
credentials: 'same-origin' // Additional fetch() options like `credentials` or `headers`
}),
cache: new InMemoryCache().restore(initialState || {})
})
}
export default function initApollo (initialState) {
// Make sure to create a new client for every server-side request so that data
// isn't shared between connections (which would be bad)
if (!process.browser) {
return create(initialState)
}
// Reuse client on the client-side
if (!apolloClient) {
apolloClient = create(initialState)
}
return apolloClient
}
The component I'm retrieving data into looks like this:
// /components/PostsList2.jsx
import { Query } from 'react-apollo'
import gql from 'graphql-tag'
export const allUsersQuery = gql`
query allUsers($first: Int!, $skip: Int!) {
allUsers(orderBy: createdAt_DESC, first: $first, skip: $skip) {
id
firstName
createdAt
}
_allUsersMeta {
count
}
}
`
export const allUsersQueryVars = {
skip: 0,
first: 10
}
export default function PostsList2 () {
return (
<Query query={allUsersQuery} variables={allUsersQueryVars}>
{({ loading, error, data: { allUsers, _allUsersMeta }, fetchMore }) => {
if (error) return <aside>Error loading users!</aside>
if (loading) return <div>Loading</div>
const areMorePosts = allUsers.length < _allUsersMeta.count
return (
<section>
<ul>
{allUsers.map((user, index) => (
<li key={user.id}>
<div>
<span>{index + 1}. </span>
<div>{user.firstName}</div>
</div>
</li>
))}
</ul>
{areMorePosts ? (
<button onClick={() => loadMorePosts(allUsers, fetchMore)}>
{' '}
{loading ? 'Loading...' : 'Show More'}{' '}
</button>
) : (
''
)}
</section>
)
}}
</Query>
)
}
function loadMorePosts (allUsers, fetchMore) {
fetchMore({
variables: {
skip: allUsers.length
},
updateQuery: (previousResult, { fetchMoreResult }) => {
if (!fetchMoreResult) {
return previousResult
}
return Object.assign({}, previousResult, {
// Append the new users results to the old one
allUsers: [...previousResult.allUsers, ...fetchMoreResult.allUsers]
})
}
})
}
Since this is a NextJS project, there's also an _app.jsx that I've wrapped in a special provider component:
// /pages._app.jsx
/* eslint-disable max-len */
import '../static/styles/fonts.scss';
import '../static/styles/style.scss';
import '../static/styles/some.css';
import CssBaseline from '#material-ui/core/CssBaseline';
import { ThemeProvider } from '#material-ui/styles';
import jwt from 'jsonwebtoken';
import withRedux from 'next-redux-wrapper';
import App, {
Container,
} from 'next/app';
import Head from 'next/head';
import React from 'react';
import { Provider } from 'react-redux';
import makeStore from '../reducers';
import mainTheme from '../themes/main-theme';
import getSessIDFromCookies from '../utils/get-sessid-from-cookies';
import getLanguageFromCookies from '../utils/get-language-from-cookies';
import getUserTokenFromCookies from '../utils/get-user-token-from-cookies';
import removeFbHash from '../utils/remove-fb-hash';
import withApolloClient from '../lib/with-apollo-client'
import { ApolloProvider } from 'react-apollo'
class MyApp extends App {
static async getInitialProps({ Component, ctx }) {
let userToken;
let sessID;
let language;
if (ctx.isServer) {
ctx.store.dispatch({ type: 'UPDATEIP', payload: ctx.req.headers['x-real-ip'] });
userToken = getUserTokenFromCookies(ctx.req);
sessID = getSessIDFromCookies(ctx.req);
language = getLanguageFromCookies(ctx.req);
const dictionary = require(`../dictionaries/${language}`);
ctx.store.dispatch({ type: 'SETLANGUAGE', payload: dictionary });
if(ctx.res) {
if(ctx.res.locals) {
if(!ctx.res.locals.authenticated) {
userToken = null;
sessID = null;
}
}
}
if (userToken && sessID) { // TBD: validate integrity of sessID
const userInfo = jwt.verify(userToken, process.env.JWT_SECRET);
ctx.store.dispatch({ type: 'ADDUSERINFO', payload: userInfo });
}
ctx.store.dispatch({ type: 'ADDSESSION', payload: sessID }); // component will be able to read from store's state when rendered
}
const pageProps = Component.getInitialProps ? await Component.getInitialProps(ctx) : {};
return { pageProps };
}
componentDidMount() {
// Remove the server-side injected CSS.
const jssStyles = document.querySelector('#jss-server-side');
if (jssStyles) {
jssStyles.parentNode.removeChild(jssStyles);
}
// Register serviceWorker
if ('serviceWorker' in navigator) { navigator.serviceWorker.register('/serviceWorker.js'); }
// Handle FB's ugly redirect URL hash
removeFbHash(window, document);
}
render() {
const { Component, pageProps, store, apolloClient } = this.props;
return (
<Container>
<Head>
// redacted for brevity
</Head>
<ThemeProvider theme={mainTheme}>
{/* CssBaseline kickstart an elegant, consistent, and simple baseline to build upon. */}
<CssBaseline />
<ApolloProvider client={apolloClient}>
<Provider store={store}>
<Component {...pageProps} />
</Provider>
</ApolloProvider>
</ThemeProvider>
</Container>
);
}
}
export default withApolloClient(withRedux(makeStore)(MyApp));
So with this setup, when I compile and run my app, it throws the following:
TypeError: Cannot read property 'allUsers' of undefined
I'm really lost! The repo is up at https://github.com/amitschandillia/proost/tree/master/web.

Related

Undefined data after client-side rendering with Next.js and WPGraphQL

I have created an app using Next.js, Apollo, and I fetch data with WPGraphQL. So far I have been able to fetch data using getStaticProps() and getServerSideProps() on pages files.
However it doesn't work with client-side rendering in Components as it either keep loading or logs data as "undefined". The weird thing is the same implementation works when I fetch data from a non-WPGraphQL url.
Here is my code.
_app.js
import 'bootstrap/dist/css/bootstrap.css';
import '../public/css/styles.css';
import Header from '../components/Header';
import Footer from '../components/Footer';
import { ApolloProvider } from "#apollo/client";
import client from "../apollo-client";
function MyApp({ Component, pageProps }) {
return (
<>
<ApolloProvider client={client}>
<Header />
<Component {...pageProps} />
<Footer />
</ApolloProvider>
</>
);
}
export default MyApp;
./apollo-client.js
import { ApolloClient, InMemoryCache } from "#apollo/client";
const client = new ApolloClient({
uri: process.env.WP_API_URL,
cache: new InMemoryCache(),
headers: {
authorization: process.env.WP_AUTHORIZATION,
},
});
export default client;
./components/ClientOnly.js
import { useEffect, useState } from "react";
export default function ClientOnly({ children, ...delegated }) {
const [hasMounted, setHasMounted] = useState(false);
useEffect(() => {
setHasMounted(true);
}, []);
if (!hasMounted) {
return null;
}
return <div {...delegated}>{children}</div>;
}
.components/Articles.js
import { useQuery, gql } from "#apollo/client";
const QUERY = gql`
query Articles {
posts(where: { categoryName: "articles", status: PUBLISH}, last: 3) {
edges {
node {
author {
node {
name
}
}
featuredImage {
node {
sourceUrl
srcSet
}
}
title
slug
}
}
}
}
`;
export default function Articles() {
const { data, loading, error } = useQuery(QUERY);
if (loading) {
return <h2>Loading...</h2>;
}
if (error) {
console.error(error);
return null;
}
const posts = data.posts.edges.map(({ node }) => node);
console.log('THis is DATA',data)
return (
<div>
...

I have successfully implemented the redux-persist with next-redux-wrapper in next js

Im getting data from the external api and storing it in the reducer.And im using redux-persist to persist the state while navigating from one page to another.But i have made left the whiteList as an empty array but all the state are being persisted?Need help
import "../assets/css/style.scss";
import "owl.carousel/dist/assets/owl.carousel.css";
import "owl.carousel/dist/assets/owl.theme.default.css";
import Layout from "../component/Layout/Layout";
import { wrapper } from "../redux/store";
import { useEffect } from "react";
import { useStore } from "react-redux";
function MyApp({ Component, pageProps }) {
const store = useStore((store) => store);
useEffect(() => {
{
typeof document !== undefined
? require("bootstrap/dist/js/bootstrap.bundle")
: null;
}
}, []);
return (
<Layout>
<Component {...pageProps} />;
</Layout>
);
}
export default wrapper.withRedux(MyApp);
import { createStore, applyMiddleware } from "redux";
import thunk from "redux-thunk";
import { composeWithDevTools } from "redux-devtools-extension";
import { persistStore, persistReducer } from "redux-persist";
import storage from "redux-persist/lib/storage";
import rootReducer from "./index";
import { createWrapper, HYDRATE } from "next-redux-wrapper";
const middleware = [thunk];
let initialState={}
// BINDING MIDDLEWARE
const bindMiddleware = (middleware) => {
if (process.env.NODE_ENV !== "production") {
return composeWithDevTools(applyMiddleware(...middleware));
}
return applyMiddleware(...middleware);
};
const makeStore = ({ isServer }) => {
if (isServer) {
//If it's on server side, create a store
return createStore(rootReducer,initialState, bindMiddleware(middleware));
} else {
//If it's on client side, create a store which will persis
const persistConfig = {
key: "root",
storage: storage,
whiteList: [],
};
const persistedReducer = persistReducer(persistConfig, rootReducer);
const store = createStore(persistedReducer,initialState, bindMiddleware(middleware));
store.__persisitor = persistStore(store); // This creates a persistor object & push that
persisted object to .__persistor, so that we can avail the persistability feature
return store;
}
};
// export an assembled wrapper
export const wrapper = createWrapper(makeStore);
If you keep the whitelist an empty array then nothing will be persisted.
You have to put inside that string array the redux reducers values you want to be persisted.
Example:
const persistConfig = {
key: 'root',
storage: storage,
whiteList: ['cart', 'form', 'user'],
};

Unable to set up Apollo Client to handle setting Authorization token in NextJS

I've been looking at this for days, and simply can't figure this out!
My apolloClient is set up nearly identical to the one found in this article. Rather than add the token stored in the browser cookies for every single request I make throughout my app, I'd like to set it during initialization of ApolloClient.
What I'm seeing however, is a graphQL error being sent from my server telling me that I haven't supplied a JWT, and when I look at the headers for the request, Authorization is blank. Ultimately, I need this to be able to work for both server- and client-side queries and mutations.
pages/_app.tsx
// third party
import type { AppProps } from 'next/app';
import Head from 'next/head';
import { ApolloProvider } from '#apollo/client';
import { MuiThemeProvider, StylesProvider } from '#material-ui/core/styles';
import CssBaseline from '#material-ui/core/CssBaseline';
import { ThemeProvider } from 'styled-components';
// custom
import Page from 'components/Page';
import theme from 'styles/theme';
import GlobalStyle from 'styles/globals';
import { useApollo } from 'lib/apolloClient';
function App({ Component, pageProps, router }: AppProps): JSX.Element {
const client = useApollo(pageProps);
return (
<>
<Head>
<title>My App</title>
</Head>
<ApolloProvider client={client}>
<MuiThemeProvider theme={theme}>
<ThemeProvider theme={theme}>
<StylesProvider>
<CssBaseline />
<GlobalStyle />
<Page>
<Component {...pageProps} />
</Page>
</StylesProvider>
</ThemeProvider>
</MuiThemeProvider>
</ApolloProvider>
</>
);
}
export default App;
pages/index.tsx
/* eslint-disable #typescript-eslint/explicit-module-boundary-types */
import { useEffect, useState } from 'react';
import Head from 'next/head';
import { GetServerSidePropsContext, NextPageContext } from 'next';
import { Box, Grid, Typography } from '#material-ui/core';
import Widget from 'components/common/Widget';
import Loading from 'components/common/Loading';
import { addApolloState, initializeApollo } from 'lib/apolloClient';
import { MeDocument } from 'generated/types';
const Home: React.FC = () => {
return (
<>
<Head>
<title>Dashboard</title>
</Head>
<Grid container spacing={3}>
... other rendered components
</Grid>
</>
);
};
export const getServerSideProps = async (
context: GetServerSidePropsContext
) => {
const client = initializeApollo({ headers: context?.req?.headers });
try {
await client.query({ query: MeDocument });
return addApolloState(client, {
props: {},
});
} catch (err) {
// return redirectToLogin(true);
console.error(err);
return { props: {} };
}
};
export default Home;
lib/apolloClient.ts
import { useMemo } from 'react';
import {
ApolloClient,
ApolloLink,
InMemoryCache,
NormalizedCacheObject,
} from '#apollo/client';
import { onError } from '#apollo/link-error';
import { createUploadLink } from 'apollo-upload-client';
import merge from 'deepmerge';
import { IncomingHttpHeaders } from 'http';
import fetch from 'isomorphic-unfetch';
import isEqual from 'lodash/isEqual';
import type { AppProps } from 'next/app';
import getConfig from 'next/config';
const { publicRuntimeConfig } = getConfig();
const APOLLO_STATE_PROP_NAME = '__APOLLO_STATE__';
let apolloClient: ApolloClient<NormalizedCacheObject> | undefined;
const createApolloClient = (headers: IncomingHttpHeaders | null = null) => {
console.log('createApolloClient headers: ', headers);
// isomorphic fetch for passing the cookies along with each GraphQL request
const enhancedFetch = async (url: RequestInfo, init: RequestInit) => {
console.log('enhancedFetch headers: ', init.headers);
const response = await fetch(url, {
...init,
headers: {
...init.headers,
'Access-Control-Allow-Origin': '*',
// here we pass the cookie along for each request
Cookie: headers?.cookie ?? '',
},
});
return response;
};
return new ApolloClient({
// SSR only for Node.js
ssrMode: typeof window === 'undefined',
link: ApolloLink.from([
onError(({ graphQLErrors, networkError }) => {
if (graphQLErrors)
graphQLErrors.forEach(({ message, locations, path }) =>
console.log(
`[GraphQL error]: Message: ${message}, Location: ${locations}, Path: ${path}`
)
);
if (networkError)
console.log(
`[Network error]: ${networkError}. Backend is unreachable. Is it running?`
);
}),
// this uses apollo-link-http under the hood, so all the options here come from that package
createUploadLink({
uri: publicRuntimeConfig.graphqlEndpoint,
// Make sure that CORS and cookies work
fetchOptions: {
mode: 'cors',
},
credentials: 'include',
fetch: enhancedFetch,
}),
]),
cache: new InMemoryCache(),
});
};
type InitialState = NormalizedCacheObject | undefined;
type ReturnType = ApolloClient<NormalizedCacheObject>;
interface IInitializeApollo {
headers?: IncomingHttpHeaders | null;
initialState?: InitialState | null;
}
export const initializeApollo = (
{ headers, initialState }: IInitializeApollo = {
headers: null,
initialState: null,
}
): ReturnType => {
console.log('initializeApollo headers: ', headers);
const _apolloClient = apolloClient ?? createApolloClient(headers);
// If your page has Next.js data fetching methods that use Apollo Client, the initial state
// get hydrated here
if (initialState) {
// Get existing cache, loaded during client side data fetching
const existingCache = _apolloClient.extract();
// Merge the existing cache into data passed from getStaticProps/getServerSideProps
const data = merge(initialState, existingCache, {
// combine arrays using object equality (like in sets)
arrayMerge: (destinationArray, sourceArray) => [
...sourceArray,
...destinationArray.filter(d => sourceArray.every(s => !isEqual(d, s))),
],
});
// Restore the cache with the merged data
_apolloClient.cache.restore(data);
}
// For SSG and SSR always create a new Apollo Client
if (typeof window === 'undefined') return _apolloClient;
// Create the Apollo Client once in the client
if (!apolloClient) apolloClient = _apolloClient;
return _apolloClient;
};
export const addApolloState = (
client: ApolloClient<NormalizedCacheObject>,
pageProps: AppProps['pageProps']
): ReturnType => {
if (pageProps?.props) {
pageProps.props[APOLLO_STATE_PROP_NAME] = client.cache.extract();
}
return pageProps;
};
export function useApollo(pageProps: AppProps['pageProps']): ReturnType {
const state = pageProps[APOLLO_STATE_PROP_NAME];
const store = useMemo(
() => initializeApollo({ initialState: state }),
[state]
);
return store;
}
I've got three console logs inside of the apolloClient.ts file. When I reload the page, the initializeApollo headers and createApolloClient headers console logs both return full (and correct) headers, including the cookie I'm after. enhancedFetch headers ONLY has { accept: '*/*', 'content-type': 'application/json'}. So that's the first oddity. I then receive my server error about a missing JWT, and all three console logs are then run twice more: initializeApollo headers is undefined, createApolloClient headers is null, and enhancedFetch headers remains the same.
My gut tells me that it's something to do with the server not receiving the header on its first go, so it messes up the following console logs (which I suppose could be client-side?). I'm kind of at a loss at this point. What am I missing??

React, Redux app works on localhost but not working on production

When i do a production build i am getting store not found error. Interestingly Redux store is not being initialised.
The error message:
{ Invariant Violation: Could not find "store" in the context of "Connect(Form(LoginForm))". Either wrap the root
component in a , or pass a custom React context provider to and the corresponding React context consumer to Connect(Form(LoginForm)) in connect options.
import Immutable from 'immutable'
import thunkMiddleware from 'redux-thunk'
import { createLogger } from 'redux-logger'
import { createStore, applyMiddleware, compose } from 'redux'
import config from 'config'
import rootReducer from './reducers/index.js'
function createMiddlewares ({ isServer }) {
let middlewares = [
thunkMiddleware
]
if (config.env === 'development' && typeof window !== 'undefined') {
middlewares.push(createLogger({
level: 'info',
collapsed: true,
stateTransformer: (state) => {
let newState = {}
for (let i of Object.keys(state)) {
if (Immutable.Iterable.isIterable(state[i])) {
newState[i] = state[i].toJS()
} else {
newState[i] = state[i]
}
}
return newState
}
}))
}
return middlewares
}
function immutableChildren (obj) {
let state = {}
Object.keys(obj).forEach((key) => {
state[key] = Immutable.fromJS(obj[key])
})
return state
}
export default (initialState = {}, context) => {
let { isServer } = context
let middlewares = createMiddlewares({ isServer })
let state = immutableChildren(initialState)
return createStore(
rootReducer,
state,
compose(applyMiddleware(...middlewares))
)
}
import withRedux from 'next-redux-wrapper'
import { withRouter } from 'next/router'
import { Provider } from 'react-redux'
import App, { Container } from 'next/app'
import { checkForPopup } from "./helpers/popup.js";
import createStore from './redux/createStore.js'
class MyApp extends App {
static async getInitialProps ({ Component, ctx }) {
return {
pageProps: Component.getInitialProps
? await Component.getInitialProps(ctx)
: {}
}
}
render() {
const { Component, pageProps, store, router } = this.props
return (
<Container>
<Provider store={store}>
<Component router={router} {...pageProps} />
</Provider>
</Container>
);
}
componentDidMount() {
checkForPopup();
}
}
export default withRedux(createStore)(
withRouter(MyApp)
)
Based on #wamba-kevin's comment, when running express in production, you prefix the start script with NODE_ENV=production. I did the same with next and it worked.

Why reducer is not being activated after dispatch()

When I submit my SignIn Form validateAndSignInUser is called and this dispatch signInUser that sends to backend an email and password to get a session token. That works right.
After signInUser returns values signInUserSuccess is dispatched, and I verified this works.
But after that the UserReducer is not beign activated and I don't understand why. What is wrong?
I have this action in my SignInFormContainer.js:
import { reduxForm } from 'redux-form';
import SignInForm from '../components/SignInForm';
import { signInUser, signInUserSuccess, signInUserFailure } from '../actions/UsersActions';
const validateAndSignInUser = (values, dispatch) => {
return new Promise ((resolve, reject) => {
let response = dispatch(signInUser(values));
response.payload.then((payload) => {
// if any one of these exist, then there is a field error
if(payload.status != 200) {
// let other components know of error by updating the redux` state
dispatch(signInUserFailure(payload));
reject(payload.data); // this is for redux-form itself
} else {
// store JWT Token to browser session storage
// If you use localStorage instead of sessionStorage, then this w/ persisted across tabs and new windows.
// sessionStorage = persisted only in current tab
sessionStorage.setItem('dhfUserToken', payload.data.token);
// let other components know that we got user and things are fine by updating the redux` state
dispatch(signInUserSuccess(payload));
resolve(); // this is for redux-form itself
}
}).catch((payload) => {
// let other components know of error by updating the redux` state
sessionStorage.removeItem('dhfUserToken');
dispatch(signInUserFailure(payload));
reject(payload.data); // this is for redux-form itself
});
});
}
const mapDispatchToProps = (dispatch) => {
return {
signInUser: validateAndSignInUser /*,
resetMe: () => {
// sign up is not reused, so we dont need to resetUserFields
// in our case, it will remove authenticated users
// dispatch(resetUserFields());
}*/
}
}
function mapStateToProps(state, ownProps) {
return {
user: state.user
};
}
// connect: first argument is mapStateToProps, 2nd is mapDispatchToProps
// reduxForm: 1st is form config, 2nd is mapStateToProps, 3rd is mapDispatchToProps
export default reduxForm({
form: 'SignInForm',
fields: ['email', 'password'],
null,
null,
validate
}, mapStateToProps, mapDispatchToProps)(SignInForm);
This three actions in UserActions.js
import axios from 'axios';
//sign in user
export const SIGNIN_USER = 'SIGNIN_USER';
export const SIGNIN_USER_SUCCESS = 'SIGNIN_USER_SUCCESS';
export const SIGNIN_USER_FAILURE = 'SIGNIN_USER_FAILURE';
export function signInUser(formValues) {
const request = axios.post('/login', formValues);
return {
type: SIGNIN_USER,
payload: request
};
}
export function signInUserSuccess(user) {
console.log('signInUserSuccess()');
console.log(user);
return {
type: SIGNIN_USER_SUCCESS,
payload: user
}
}
export function signInUserFailure(error) {
console.log('signInUserFailure()');
console.log(error);
return {
type: SIGNIN_USER_FAILURE,
payload: error
}
}
And this is my reducer UserReducer.js
import {
SIGNIN_USER, SIGNIN_USER_SUCCESS, SIGNIN_USER_FAILURE,
} from '../actions/UsersActions';
const INITIAL_STATE = {user: null, status:null, error:null, loading: false};
export default function(state = INITIAL_STATE, action) {
let error;
switch(action.type) {
case SIGNIN_USER:// sign in user, set loading = true and status = signin
return { ...state, user: null, status:'signin', error:null, loading: true};
case SIGNIN_USER_SUCCESS://return authenticated user, make loading = false and status = authenticated
return { ...state, user: action.payload.data.user, status:'authenticated', error:null, loading: false}; //<-- authenticated
case SIGNIN_USER_FAILURE:// return error and make loading = false
error = action.payload.data || {message: action.payload.message};//2nd one is network or server down errors
return { ...state, user: null, status:'signin', error:error, loading: false};
default:
return state;
}
}
Reducers combined:
import { combineReducers } from 'redux';
import { UserReducer } from './UserReducer';
import { reducer as formReducer } from 'redux-form';
const rootReducer = combineReducers({
user: UserReducer,
form: formReducer // <-- redux-form
});
export default rootReducer;
configureStore.js
import {createStore, applyMiddleware, combineReducers, compose} from 'redux';
import thunkMiddleware from 'redux-thunk';
import {devTools, persistState} from 'redux-devtools';
import rootReducer from '../reducers/index';
let createStoreWithMiddleware;
// Configure the dev tools when in DEV mode
if (__DEV__) {
createStoreWithMiddleware = compose(
applyMiddleware(thunkMiddleware),
devTools(),
persistState(window.location.href.match(/[?&]debug_session=([^&]+)\b/))
)(createStore);
} else {
createStoreWithMiddleware = applyMiddleware(thunkMiddleware)(createStore);
}
export default function configureStore(initialState) {
return createStoreWithMiddleware(rootReducer, initialState);
}
index.js:
import React from 'react';
import ReactDOM from 'react-dom';
import {Provider} from 'react-redux';
import { Router, browserHistory } from 'react-router';
import routes from './routes';
import configureStore from './store/configureStore';
import {renderDevTools} from './utils/devTools';
const store = configureStore();
ReactDOM.render(
<div>
{/* <Home /> is your app entry point */}
<Provider store={store}>
<Router history={browserHistory} routes={routes} />
</Provider>
{/* only renders when running in DEV mode */
renderDevTools(store)
}
</div>
, document.getElementById('main'));

Resources