I'm currently using redux-persist to persistent data storage in my react-redux application. I have set it up according to the documentation. I have tested it on a separate project with npx create-react-app. The persistent storage was working properly.
When I integrate it in my actual working project, the state saving doesn't work. Can someone help me with this?
Here is my store.js:
import { createStore, applyMiddleware, compose } from 'redux';
import { routerMiddleware } from 'react-router-redux';
import thunk from 'redux-thunk';
import rootReducers from './reducers';
// imports for persistance redux state
import { persistStore, persistReducer } from 'redux-persist'
import storage from 'redux-persist/lib/storage' // defaults to localStorage for web
const middleware = [thunk, routerMiddleware(history)];
const initialState = {};
//config for redux-persist
const persistConfig = {
key: 'root',
storage,
}
const persistedReducer = persistReducer(persistConfig, rootReducers)
function composeWithApplyMiddlewares() {
if (window.__REDUX_DEVTOOLS_EXTENSION__) {
return compose(applyMiddleware(...middleware), window.__REDUX_DEVTOOLS_EXTENSION__());
}
return compose(applyMiddleware(...middleware));
}
const createMyStore = () =>
createStore(
persistedReducer,
initialState,
composeWithApplyMiddlewares(),
);
let persistor = persistStore(createMyStore());
export default createMyStore();
index.js:
import React from 'react';
import { render } from 'react-dom';
import { Provider } from 'react-redux';
import { syncHistoryWithStore } from 'react-router-redux';
import { Router, Route, browserHistory } from 'react-router';
import RandomID from 'random-id';
import 'normalize.css/normalize.css';
import 'plottable/plottable.css';
import store from './store';
import SignInContainer from './containers/Auth/Signin';
import SignUpContainer from './containers/Auth/Signup';
import ForgotPasswordContainer from './containers/Auth/ForgotPassword';
import ResetPasswordContainer from './containers/Auth/ResetPassword';
import CompleteSignupContainer from './containers/Auth/CompleteSignup';
import UsersListContainer from './containers/Users/List';
import CreateEndpointContainer from './containers/Endpoints/CreateEndpoint';
import TestMessagesContainer from './containers/TestMessages';
import Dashboard from './containers/Dashboard';
import initialize from './actions/initialize';
import Setting from './containers/Setting/Main';
const root = document.getElementById('root');
const history = syncHistoryWithStore(browserHistory, store);
const rand = RandomID(16, 'A0');
localStorage.setItem('authStateKey', rand);
const reduxOauthConfig = {
backend: {
apiUrl: 'http://0.0.0.0:4000', // TODO: Change backend URL here
signOutPath: null, // Add signout path to destroy session token
tokenValidationPath: '/svr/ping',
authProviderPaths: {
facebook: '/svr/auth/facebook',
github: '/svr/auth/github',
gitlab: '/svr/auth/gitlab',
google: '/svr/auth/google',
twitter: '/svr/auth/twitter',
},
},
};
const requireAuth = (nextState, replace) => {
const state = store.getState();
const isSignedIn = state.auth.authUser.isSignedIn;
// const isSignupCompleted = state.auth.authUser.isSignupCompleted;
// Complete signup first before user can do anything
// if (!isSignupCompleted && nextState.location.pathname !== '/complete-signup') {
// replace({
// pathname: '/complete-signup',
// state: { nextPathname: nextState.location.pathname },
// });
// }
if (!isSignedIn) {
replace({
pathname: '/signin',
state: { nextPathname: nextState.location.pathname },
});
}
};
const publicOnly = (nextState, replace) => {
const state = store.getState();
const isSignedIn = state.auth.authUser.isSignedIn;
if (isSignedIn) {
replace({
pathname: '/',
state: { nextPathname: nextState.location.pathname },
});
}
};
store.dispatch(initialize(reduxOauthConfig)).then(
() => {
render(
<Provider store={store}>
<Router history={history}>
<Route exact path="/signup" component={SignUpContainer} onEnter={publicOnly} />
<Route exact path="/signin" component={SignInContainer} onEnter={publicOnly} />
<Route exact path="/forgot-password" component={ForgotPasswordContainer} onEnter={publicOnly} />
<Route exact path="/reset-password" component={ResetPasswordContainer} onEnter={publicOnly} />
<Route exact path="/complete-signup" component={CompleteSignupContainer} onEnter={requireAuth} />
<Route exact path="/users" component={UsersListContainer} onEnter={requireAuth} />
<Route exact path="/chat" component={TestMessagesContainer} onEnter={requireAuth} />
<Route exact path="/add-endpoint" component={CreateEndpointContainer} onEnter={requireAuth} />
<Route exact path="/" component={Dashboard} onEnter={requireAuth} />
<Route exact path="/:userId/setting" component={Setting} onEnter={publicOnly} />
</Router>
</Provider>
, root,
);
},
);
reducers/index.js:
import { combineReducers } from 'redux';
import { routerReducer } from 'react-router-redux';
import message from './message';
import users from './users';
import endpoints from './endpoints';
import toasts from './toasts';
import auth from './auth';
export default combineReducers({
routing: routerReducer,
users,
endpoints,
ui: toasts,
chat: message,
auth,
});
Thanks for your attention.
Related
An unauthenticated user can still see the "/account" page for a few seconds I mean there is still a delay of a few seconds before the unauthenticated user is completely redirected to another page. even though I have used ProtectedRoute, how can I make unauthenticated users unable to see the "/account" page at all without delay? i use react-router-dom 6.6.1 and firebase auth
here is for the app.js
import { ThemeProvider } from '#mui/material/styles';
import React from 'react';
import { BrowserRouter as Router, Route, Routes, Navigate} from 'react-router-dom'
import Home from './components/Home'
import Account from './components/Account';
import Signin from './components/Home/Signin';
import theme from './theme';
import { AuthContextProvider } from './components/Context/AuthContext';
import ProtectedRoute from './components/ProtectedRoute';
function App() {
return (
<ThemeProvider theme={theme}>
<AuthContextProvider>
<Router>
<Routes>
<Route path='/' element={<Home />} />
<Route path='/signin' element={<Signin />} />
<Route path='/account' element={<ProtectedRoute><Account /></ProtectedRoute>} />
</Routes>
</Router>
</AuthContextProvider>
</ThemeProvider>
)
}
export default App
and this is for the ProtectedRoute.js
import React from 'react';
import { Navigate } from 'react-router-dom';
import { UserAuth } from './Context/AuthContext';
const ProtectedRoute = ({ children }) => {
const { user } = UserAuth();
if (!user) {
return <Navigate to='/' />;
}
return children;
};
export default ProtectedRoute;
this if for the UserAuth :
import { createContext, useContext, useEffect, useState } from 'react';
import {
createUserWithEmailAndPassword,
signInWithEmailAndPassword,
onAuthStateChanged,
} from 'firebase/auth';
import { auth } from '../../firebase';
const UserContext = createContext();
export const AuthContextProvider = ({ children }) => {
const [user, setUser] = useState({});
const createUser = (email, password) => {
return createUserWithEmailAndPassword(auth, email, password);
};
const signIn = (email, password) => {
return signInWithEmailAndPassword(auth, email, password)
}
useEffect(() => {
const unsubscribe = onAuthStateChanged(auth, (currentUser) => {
setUser(currentUser);
});
return () => {
unsubscribe();
};
}, []);
return (
<UserContext.Provider value={{ createUser, user, signIn }}>
{children}
</UserContext.Provider>
);
};
export const UserAuth = () => {
return useContext(UserContext);
};
I want to redirect unauthenticated users to the main page without delay
it’s probably due to the fact that your guard is validating off of !user however your initial auth state has user as {}. In this case an empty object will validate as truthy.
You could either set the initial state of user as null or you could update th guard validation to check for a value on user like if (!user.id) …
Render Protected and Base routes conditional so unauthenticated user never have access of Protected routes.
Use the following code.
Always make separate file for routing for better code readability and Separation of concerns.
BaseRoutes.js
import React from 'react'
import { Route } from 'react-router-dom'
import Home from './components/Home'
import Signin from './components/Home/Signin';
const BaseRoutes = (
<>
<Route path='/' element={<Home />} />
<Route path='/signin' element={<Signin />} />
</>
)
export default BaseRoutes
ProtectedRoutes.js
import React from 'react';
import { Route } from 'react-router-dom';
import Account from './components/Account';
const ProtectedRoutes = () => {
return (
<>
<Route path='/account' element={<Account />} />
</>
)
};
export default ProtectedRoutes;
Routes.js
import React from 'react'
import { Navigate, Route, Routes, BrowserRouter } from 'react-router-dom'
import { UserAuth } from './Context/AuthContext';
import ProtectedRoutes from './components/ProtectedRoutes';
import BaseRoutes from './components/BaseRoutes';
const Router = () => {
const { user } = UserAuth();
const isUser = Boolean(user)
const redirectUrl = isUser ? '/account' : '/signin'
return (
<BrowserRouter>
<Routes>
{isUser ? <BaseRoutes /> : <ProtectedRoutes />}
<Route
path="*"
element={<Navigate to={redirectUrl} />}
/>
</Routes>
</BrowserRouter>
)
}
export default Router
app.js
import React from 'react';
import { AuthContextProvider } from './components/Context/AuthContext';
import { ThemeProvider } from '#mui/material/styles';
import theme from './theme';
import Router from './routes'
function App() {
return (
<ThemeProvider theme={theme}>
<AuthContextProvider>
<Router />
</AuthContextProvider>
</ThemeProvider>
)
}
export default App
with my enterprise we are passing to Keycloak & I'm making test on our existing apps.
Anyway, I am testing actually on a React App which is working to a Symfony API. I created the two clients in Keycloak, with the roles, and users.I adapted the API authenticator for Keycloak, and it's working great.
On front side, I changed the Axios middleware to send the Keycloak's token to the API.
When I'm testing my requests on Insomnia, I get my token and refresh token, and all requests that I'm sending works nicely too ! And on Keycloak's Admin panel the sessions are active as well.
Problem is on client side. I installed keycloak-js and #react-keycloak/web because of the hooks that are usefull for me, to test if user is authenticated, to get roles, to protect pages depending on the role etc...
The thing is when I'm login, I get my tokens, session is active on KC side, but KC-js adapter don't tell user is authenticated, I think because user is not login with keycloak.login which is the Login page of KC. I don't want to log on his page, application already have its own login page. But whatever I'm testing, even I ge my token and the session is well active on KC, the hook keycloak.authenticated is always on false.
Have you any suggests on that ?
index.tsx
import ReactDOM from 'react-dom/client';
import 'bootstrap/dist/css/bootstrap.css'
import './index.css';
import './i18n';
import App from './App';
import i18next from 'i18next';
import { I18nextProvider } from 'react-i18next';
/*------ STORE REDUX PART ------*/
import { Provider, TypedUseSelectorHook } from "react-redux";
import { useSelector } from 'react-redux';
import { useDispatch } from 'react-redux';
import rootReducer from './Api/Reducers/rootReducer';
import { configureStore, PreloadedState } from "#reduxjs/toolkit"
import { ReactKeycloakProvider } from '#react-keycloak/web';
import keycloak from './components/keycloak';
const root = ReactDOM.createRoot(
document.getElementById('root') as HTMLElement
);
export const makeStore = (preloadedState?: PreloadedState<RootState>) => {
return configureStore({
reducer: rootReducer,
middleware: (getDefaultMiddleware) => getDefaultMiddleware({
immutableCheck: false,
serializableCheck: false
}),
devTools: true,
preloadedState
})
}
const store = makeStore();
const tokenLogger = (tokens: unknown) => {
console.log('onKeycloakTokens', tokens)
}
const keycloakProviderInitConfig = {
onLoad: 'check-sso',
}
root.render(
<ReactKeycloakProvider
authClient={keycloak}
onTokens={tokenLogger}
initOptions={keycloakProviderInitConfig }
>
<Provider store={store}>
<I18nextProvider i18n={i18next}>
<App />
</I18nextProvider>
</Provider>
</ReactKeycloakProvider>
);
export default store;
export type RootState = ReturnType<typeof rootReducer>;
export type AppStore = ReturnType<typeof makeStore>;
export type AppDispatch = typeof store.dispatch;
export const useAppSelector: TypedUseSelectorHook<RootState> = useSelector;
export const useAppDispatch = () => useDispatch<AppDispatch>();
intercept.tsx
import axios from "axios";
export const instance = axios.create({
baseURL: process.env.REACT_APP_API_ADDRESS,
headers: {'X-gravitee-api-key': process.env.REACT_APP_API_KEY}
});
const kcInstance = axios.create({
baseURL: process.env.REACT_APP_KEYCLOAK_ADDRESS,
headers: {
'Content-Type' : 'application/x-www-form-urlencoded',
'X-gravitee-api-key': process.env.REACT_APP_API_KEY
}
})
/*----- API Middleware Request -----*/
/*----- Pour tester le SessionID -----*/
kcInstance.interceptors.request.use(
(config) => {
let profile = localStorage.getItem('profile');
let kcStorage = JSON.parse(localStorage.getItem('keycloak'));
if(kcStorage){
let kcToken = kcStorage.access_token;
config.headers['Authorization'] = `Bearer ` + kcToken;
if(profile) {
const userDatas = JSON.parse(profile);
let token = userDatas.sessionId;
let language = localStorage.getItem('language');
let datas = {
sessionId: token,
language: language
}
const bearerInstance = axios.create({
baseURL: process.env.REACT_APP_API_ADDRESS,
headers: {
'X-gravitee-api-key': process.env.REACT_APP_API_KEY,
'Authorization': `Bearer ` + kcToken
}
})
bearerInstance.post('/api/check-session', datas)
.then(res => {
if(res.status === 200) return axios(config);
})
.catch(
(error) => {
if(error.response?.status === 404 && error.response?.data.message.includes("Session")) {
localStorage.clear();
window.location.href= ("/");
}
else if(error.response?.status === 404 && error.response?.data.message.includes("session")) {
localStorage.clear();
window.location.href= ("/");
}
})
}
}
return config;
},
error => {
console.log('error request :' + error)
Promise.reject(error)
});
App.tsx
import { Suspense, useEffect, useState } from 'react';
import SiderLayout from './components/Menu/Layout';
import Assistance from './pages/Assistance';
import ChangePassword from './pages/ChangePassword';
import ForgetPassword from './pages/ForgetPassword';
import Login from './pages/Login';
import RenewIDToken from './pages/RenewIDToken';
import {
BrowserRouter as Router,
Navigate,
Route,
Routes
} from 'react-router-dom'
import ConsultOrder from './pages/ConsultOrder';
import ConsultRepair from './pages/ConsultRepair';
import StockManagement from './pages/StockManagement';
import Consumptions from './pages/Consumptions';
import Contractuals from './pages/Contractuals';
import DetailSerial from './pages/DetailSerial';
import DetailsMortgage from './pages/DetailsMortgage';
import Home from './pages/Home';
import GlobalStyles from './GlobalStyles';
import { useKeycloak } from '#react-keycloak/web';
import PrivateRoute from './Keycloak/PrivateRoute';
import HasAccess from './Keycloak/HasAccess';
function App() {
const { initialized, keycloak } = useKeycloak();
const isLoggedIn = keycloak.authenticated;
if(!initialized) {
return <h3>Chargement... !!!</h3>
}
return (
<Suspense fallback="loading">
<Router>
<GlobalStyles/>
<Routes>
<Route path="/assistance" element={<Assistance/>} />
<Route path="/forget-password" element={<ForgetPassword/>} />
<Route path="/change-password" element={<ChangePassword/>} />
<Route path="/renew-token" element={<RenewIDToken/>} />
{ isLoggedIn ?
<Route path="/" element={<SiderLayout profil={HasAccess(['app-admin'])} />}>
<Route index element={<Home/>}/>
<Route path="/consult-repair" element={<ConsultRepair/>} />
<Route path="/stock-management" element={<StockManagement/>} />
<Route path="/detail-serial/:nno/:sn" element={<DetailSerial/>} />
<Route path="/detail-mortgage/:mortgageNumber" element={<DetailsMortgage/>} />
{ HasAccess(['app-admin']) &&
<>
<PrivateRoute path="/consult-order" component={<ConsultOrder/>} roles={["app-admin"]} />
<Route path="/consumptions-indicators" element={<Consumptions/>} />
<Route path="/contractual-indicators" element={<Contractuals/>} />
</>
}
</Route>
:
<Route path="/" element={<Login/>} /> }
<Route path="*" element={<Navigate to="/" replace/>} />
</Routes>
</Router>
</Suspense>
);
}
export default App;
I have a ConnectedRouter to which I wanted to add hashes to all the routes so I added the HashRouter component like this:
// #flow
import React from 'react';
import { Router, Route,Switch } from 'react-router'
import { HashRouter } from 'react-router-dom'
import { ConnectedRouter } from 'react-router-redux';
import { routerActions } from 'react-router-redux';
import { UserAuthWrapper } from 'redux-auth-wrapper';
import StudiesViewContainer from './components/views/studies/StudiesViewContainer';
import NotificationsViewContainer from './components/views/notifications/NotificationsViewContainer';
import UserView from './components/views/user/UserView';
import StudyViewContainer from './components/views/studies/StudyViewContainer';
import { getUser } from './reducers';
import LoginView from './components/views/login';
import NotFoundView from './components/views/notFound';
import ForbiddenView from './components/views/forbidden';
const UserIsAuthenticated = UserAuthWrapper({
authSelector: getUser,
redirectAction: routerActions.replace,
failureRedirectPath: '/',
wrapperDisplayName: 'UserIsAuthenticated'
});
const configRouter = (history: Object) => {
return () =>
<ConnectedRouter history={ history }>
<HashRouter>
<Switch>
<Route path="/studies" component={ StudiesViewContainer } />
<Route path="/study/:id" component={ StudyViewContainer } />
<Route path="/user" component={ UserView } />
<Route path="/notifications" component={ NotificationsViewContainer } />
<Route path="/forbidden" component={ ForbiddenView } />
<Route path="/not-found" component={ NotFoundView } />
<Route path="/" component={ LoginView } />
<Route path="*" component={ NotFoundView } />
</Switch>
</HashRouter>
</ConnectedRouter>
};
export default configRouter;
The problem is that when I do something like this:
push('studies')
The route does not add the hash and the new components are not rendered.
I added the browser history to my store, here is the configureStore file:
// #flow
import { createStore, applyMiddleware, compose } from 'redux';
import thunk from 'redux-thunk';
import createHistory from 'history/createBrowserHistory'
import { routerMiddleware } from 'react-router-redux';
import createSagaMiddleware from 'redux-saga'
import {
persistStore,
autoRehydrate,
createTransform
} from 'redux-persist';
import mainSaga from '../sagas';
import reducer from '../reducers';
const history = createHistory();
const routingMiddleware = routerMiddleware(history);
const sagaMiddleware = createSagaMiddleware();
// Remove error field from persisted auth substate
let authTransform = createTransform(
(inboundState, key) =>
key === 'auth' ?
{ ...inboundState, error: undefined }:
inboundState,
outboundState => outboundState,
{
whitelist: [
'auth',
'permissions'
]
}
);
const configureStore = (): Promise<*> => {
let middlewares = [routingMiddleware,thunk, sagaMiddleware ];
let composeEnhancers = compose;
if(process.env.NODE_ENV !== 'production') {
composeEnhancers = window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__ || compose;
}
const store = createStore(
reducer,
composeEnhancers(
applyMiddleware(...middlewares),
autoRehydrate()));
sagaMiddleware.run(mainSaga);
return new Promise(resolve => {
persistStore(
store, {
whitelist: ['auth', 'permissions'],
debounce: 500,
transforms: [
authTransform
]
},
() => resolve({ store, history })
);
});
};
export default configureStore;
Can anyone help me get the push working as expected?
I am using the following versions of router in package json:
"react-router": "^4.2.0",
"react-router-dom": "^4.2.2",
"react-router-redux": "next",
I had a similar problem, I solved it by using
import createHistory from 'history/createHashHistory'
instead of
import createHistory from 'history/createBrowserHistory'
Also added withRouter when exporting components as suggested in
https://github.com/ReactTraining/react-router/blob/master/packages/react-router/docs/guides/blocked-updates.md
I added the browser history to my store, here is the configureStore file:
Most likely that issue lies on difference between history-object, used in router, and proposed in redux state. Then history created in redux side const history = createHistory();, it seems that it had not linked with ConnectedRouter object, but it can't be followed by supposed code.
If valid history object is passed to ConnectedRouter, try to check that subsequent router actions are to intercepted with saga process or other middleware. Try to follow on emitting actions flow by https://github.com/gaearon/redux-devtools tool.
Also you can try to perform manual updating, by adding root component with full lifecycle (Inherited from React.Component class), and add there following code:
this.props.history.listen((location, action) => {
console.log("on route change");
});
Then you can discover had router state been changed, and if action is taken, just perform forceUpdate method in root component. Also consult this documentation: https://github.com/ReactTraining/react-router/blob/master/packages/react-router/docs/guides/blocked-updates.md
I'm managing my React application route with react-router and react-router-redux.
I setting router with following configuration:
import {Provider} from 'react-redux'
import {Router, browserHistory} from 'react-router'
import {createStore, combineReducers} from 'redux'
import {routerReducer} from 'react-router-redux';
import {syncHistoryWithStore} from 'react-router-redux'
import Reducers from './reducers'; // contains all my reducers,
const store = createStore(combineReducers(
{...Reducers, routerReducer}
));
const history = syncHistoryWithStore(browserHistory, store);
const AppRoute = (
<Provider store={store}>
<Router history={history}>
<Route path='login' components={PageLogin}/>
<Route path='system' components={PageSystem}>
<Route path="profile" component={PageProfile}/>
</Route>
</Router>
</Provider>
);
export default AppRoute;
When I try dispatch any router function:
import React from 'react';
import {Link, Route} from 'react-router';
import {connect} from 'react-redux';
import {replace, push} from 'react-router-redux';
class SystemMenu extends React.Component {
constructor(props) {
super(props);
this.logout = this.logout.bind(this);
}
logout() {
this.props.logout();
}
render() {
return (
<ul className="system-menu">
<li className={this.isActual("/system/profile")}>
<Link to="/system/profile">Profile</Link>
</li>
<li className={this.isActual("/system/unit")}>
<Link to="/system/unit">Units</Link>
</li>
<li>
<button onClick={this.logout}>Logout</button>
</li>
</ul>
)
}
}
const mapStateToProps = (state) => {
return {
router: state.router,
actualPage: state.menu.actualPage
};
};
const mapDispatchToProps = (dispatch) => {
return {
logout() {
dispatch(push('/login'));
}
};
};
export default connect(mapStateToProps, mapDispatchToProps)(SystemMenu);
When I click que Link component, pages is changing, But when I click the Logout button to debug, is executing dispatch(push('/login')); line, but nothing has happening, and no occur any error in the console.
What I worse in the configuration?
I tried console.log the logout inside the mapDispatchToProps function, and returning logout is not defined
const mapDispatchToProps = (dispatch) => {
console.log(push, logout);
return {
logout() {
dispatch(push('/login'));
}
};
};
combineReducers should take an object as the first argument, like this:
const store = createStore(
combineReducers({
...reducers,
routing: routerReducer
})
)
You're giving it an Array instead.
I suspect the reason why push doesn't work is because the router reducer is not registered properly.
I resolved this problem.
This problem is occurring because I has not using react-routerredux` middleware, so when I dispatching, maybe is not updating browser URL by state.
Following my route configuration:
import {Provider} from 'react-redux'
import {Router, browserHistory} from 'react-router'
import {createStore, combineReducers, applyMiddleware} from 'redux'
import {routerReducer} from 'react-router-redux';
import {syncHistoryWithStore, routerMiddleware} from 'react-router-redux'
import Reducers from './reducers'; // contains all my reducers,
const middleware = [routerMiddleware(browserHistory)];
const store = createStore(combineReducers(Reducers), compose(
applyMiddleware(...middleware)
));
const history = syncHistoryWithStore(browserHistory, store);
const AppRoute = (
<Provider store={store}>
<Router history={history}>
<Route path='login' components={PageLogin}/>
<Route path='system' components={PageSystem}>
<Route path="profile" component={PageProfile}/>
</Route>
</Router>
</Provider>
);
export default AppRoute;
Trying to hook up a React component to my Redux state, but I am getting the error:
Uncaught Error: Expected the reducer to be a function
Not quite sure where I am going wrong:
import React from 'react';
import { render } from 'react-dom';
import { Router, Route, IndexRoute, browserHistory } from 'react-router';
import { Provider } from 'react-redux';
import { createStore, applyMiddleware } from 'redux';
import ReduxPromise from 'redux-promise';
import store, { history } from './store';
// Import components
import App from './components/App';
import Expenditure from './components/Expenditure';
import Income from './components/Income';
import Transactions from './components/Transactions';
import Single from './components/Single';
// import css
import css from './styles/style.styl';
const createStoreWithMiddleware = applyMiddleware(ReduxPromise)(createStore);
const router = (
<Provider store={createStoreWithMiddleware(store)}>
<Router history={history}>
<Route path="/" component={App}>
<IndexRoute component={Expenditure} />
<Route path="/income" component={Income} />
<Route path="/expenditure" component={Expenditure} />
<Route path="/transactions" component={Transactions} />
<Route path="/expenditure/:id" component={Single} />
<Route path="/income/:id" component={Single} />
<Route path="/transaction/:id" component={Single} />
</Route>
</Router>
</Provider>
);
render(router, document.getElementById('root'));
My rootReducer:
import { combineReducers } from 'redux';
import { routerReducer } from 'react-router-redux';
import expenditure from './expenditure';
const rootReducer = combineReducers({
expenditure,
routing: routerReducer
});
export default rootReducer;
And my actionCreators.js:
import axios from 'axios';
export const FETCH_EXPENDITURE = 'FETCH_EXPENDITURE';
export function fetchExpenditure() {
const request = axios.get('/api/expenditure');
return {
type: FETCH_EXPENDITURE,
payload: request
}
};
Then in my component, I am using connect to pull in the Redux state as props with the following below the Expenditure react class:
function mapStateToProps(state) {
console.log('state');
return {
expenditure: state.expenditure.all
};
}
function mapDispatchToProps(dispatch) {
return bindActionCreators(actionCreators, dispatch);
}
export default connect(mapStateToProps, mapDispatchToProps)(Expenditure);
Any tips would be incredible :)
are you using applyMiddleware correctly?
the docs have it passing applyMiddleware to the createStore function, not the other way around. i also dont see you using your rootReducer anywhere at all
try changing
const createStoreWithMiddleware = applyMiddleware(ReduxPromise)(createStore);
to something like
// import rootReducer from '/path/to/rootReducer.js';
const createStoreWithMiddleware = createStore(
rootReducer,
[ ... ], //=> initial state
applyMiddleware(ReduxPromise)
)
then change your router input to
<Provider store={createStoreWithMiddleware}>
...
</Provider>