Navbar is not re-rendering on router location change - reactjs

I want to display userName and changed the button text to 'logout' from 'login' in Navbar(nav component) when user is logged in. I tired to re-render the Nav Component by pushing the router history location to homepage on user logged in.
this.props.history.push('/')
Then User can successfully log in, user data is saved in localStorage and the router location is changed. data is saved in localStorage, but the component is not re-rendering.
Here is what I have tried.
tried to downgrade 'connected-react-router' to 'v6.0.0' but it threw me some errors. so put it back to the latest version '^6.4.0'.
tried to add Navbar code in App.js which is the parents component.
But the app.js itself is not re-rendering either on user logged in.
app.js
import React from 'react';
import { connect } from 'react-redux';
/* --- Components --- */
import Nav from './src/components/nav';
import Loader from './src/shared/loader';
import './styles/main.scss';
const FlashMessagesContainer = Loader({
loader: () =>
import('./src/shared/flassMessagesContainer' /* webpackChunkName: 'FlashMessagesContainer' */),
});
const App = (props, { isOnModal }) => (
<div id="app">
<Nav />
{!isOnModal && (
<div className="flex justify-center">
<FlashMessagesContainer />
</div>
)}
{props.children}
</div>
);
const mapPropsToState = state => ({
isOnModal: state.modal.show,
});
export default connect(
mapPropsToState,
null,
)(App);
nav.js
import React, { Component } from 'react';
import { connect } from 'react-redux';
import { Link, withRouter } from 'react-router-dom';
/* --- Components --- */
import {
isLoggedIn,
getCompanyName,
clearLocalStorage,
} from '../../localStorage';
class Nav extends Component {
handleUserLogout = async ev => {
ev.preventDefault();
await clearLocalStorage();
return this.props.history.push('/login');
};
render() {
return (
<div className="nav">
{isLoggedIn() ? (
<div className="flex">
<p className="mr3 mt2">
안녕하세요. <span className="b">{getCompanyName()}</span>
 님,
</p>
<button
type="button"
className="login-btn td-none c-text br f-mini"
onClick={this.handleUserLogout}
>
로그아웃
</button>
</div>
) : (
<Link className="login-btn td-none c-text br f-mini" to="/login">
로그인
</Link>
)}
</div>
);
}
}
export default withRouter(Nav);
localStorage.js
export const getToken = () => localStorage.token;
export const getCompanyName = () => localStorage.companyName;
export const isLoggedIn = () => !!localStorage.token;
export const saveUserNameAndToken = userData => {
localStorage.setItem('token', userData.token);
localStorage.setItem('companyName', userData.companyName);
};
export const clearLocalStorage = () => localStorage.clear();
index.js
import React from 'react';
import { render } from 'react-dom';
import { Provider } from 'react-redux';
import { ConnectedRouter as Router } from 'connected-react-router';
import { MuiThemeProvider, createMuiTheme } from '#material-ui/core/styles';
import configureStore, { history } from './store';
import Routes from './routes';
const theme = createMuiTheme({
...
});
const store = configureStore();
const root = document.createElement('div');
document.body.appendChild(root);
render(
<Provider store={store}>
<MuiThemeProvider theme={theme}>
<Router history={history}>
<Routes />
</Router>
</MuiThemeProvider>
</Provider>,
root,
);
routes.js
import React from 'react';
import { Switch, Route } from 'react-router-dom';
import { hot, setConfig } from 'react-hot-loader';
import { withRouter } from 'react-router';
/* --- Components --- */
import App from './app';
import Loader from './src/shared/loader';
const Home = Loader({
loader: () =>
import('./src/components/home/homeContainer' /* webpackChunkName: 'Home' */),
});
...
const routes = props => (
<div>
<App history={props.history} />
<Switch>
<Route exact path="/" component={Home} />
<Route path="/login" render={props => <Login {...props} />} />
<Route path="/users/account" component={Users} />
<Route component={NoMatch} />
</Switch>
</div>
);
const Routes =
!module.hot || process.env.NODE_ENV === 'production'
? routes
: hot(module)(routes);
export default withRouter(Routes);
I am expecting Nav component to be re-rendered when router location changes.
react: ^16.8.6
react-dom: ^16.8.6
react-loadable: ^5.5.0
react-redux: ^6.0.0
react-router-dom: ^4.3.1
redux: ^4.0.0
connected-react-router: ^6.4.0

I fixed it by saving 'isLoggedIn' state in redux store. And to persist the state from redux store on page refresh, I used 'redux-persist'.
Now Nav component is re-rendering on router location change and the userName data is saved even on page refresh.
authReducer.js
const initialState = {
isLoggedIn: false,
userName: '',
};
const auth = (state = initialState, action) => {
switch (action.type) {
case types.USER_LOGIN:
return {
...state,
isLoggedIn: true,
userName: action.payload,
};
case types.USER_LOGOUT:
return {
...state,
isLoggedIn: false,
userName: '',
};
default:
return state;
}
};
nav.js
...
{this.props.isLoggedIn ? (
...
) : (
...
)}
...
const mapStateToProps = state => ({
isLoggedIn: state.auth.isLoggedIn,
userName: state.auth.userName,
});
const mapDispatchToProps = dispatch => ({
userLogout: () => dispatch(userLogout()),
});
export default withRouter(
connect(
mapStateToProps,
mapDispatchToProps,
)(Nav),
);
[ redux-persist SetUp ]
store.js
import { applyMiddleware, createStore } from 'redux';
import thunkMiddleware from 'redux-thunk';
import { createLogger } from 'redux-logger';
import { composeWithDevTools } from 'redux-devtools-extension';
import { routerMiddleware } from 'connected-react-router';
import createBrowserHistory from 'history/createBrowserHistory';
import { persistStore, persistReducer } from 'redux-persist';
import storage from 'redux-persist/lib/storage';
import createRootReducer from './src/reducers';
export const history = createBrowserHistory();
const persistConfig = {
key: 'root',
storage,
};
const persistedReducer = persistReducer(
persistConfig,
createRootReducer(history),
);
const store = createStore(
persistedReducer,
composeWithDevTools(
applyMiddleware(
routerMiddleware(history),
thunkMiddleware,
createLogger({
predicate: () => process.env.NODE_ENV === 'development',
collapsed: true,
}),
),
),
);
export const persistor = persistStore(store);
export default store;
index.js
import React from 'react';
import { render } from 'react-dom';
import { Provider } from 'react-redux';
import { ConnectedRouter as Router } from 'connected-react-router';
import { MuiThemeProvider, createMuiTheme } from '#material-ui/core/styles';
import { PersistGate } from 'redux-persist/lib/integration/react';
import store, { history, persistor } from './store';
import Routes from './routes';
import Loading from './src/shared/loading';
const theme = createMuiTheme({
palette: {
primary: { main: '#AE9A65' },
secondary: { main: '#ee9105' },
success: { main: '#43A047' },
warning: { main: '#FFA000' },
error: { main: '#ed4337' },
info: { main: '#2196F3' },
},
});
const root = document.createElement('div');
document.body.appendChild(root);
render(
<Provider store={store}>
<PersistGate loading={<Loading />} persistor={persistor}>
<MuiThemeProvider theme={theme}>
<Router history={history}>
<Routes />
</Router>
</MuiThemeProvider>
</PersistGate>
</Provider>,
root,
);

Related

opening devtool causes webpage to reload (Google Chrome)

I'm having a issue and im not sure the problem is chrome, react or macOs
When i open devtools my component rerender. I have simple code about redux-persist
Summary: Im trying to open second browser when Page1 renders if user closes Page2, a button inside Page1 will be active to open Page2 on second tab.
When user closes/refreshs Page1 tab Page2 tab will also close and clear the persist:root
Im using:
Chrome:Version 107.0.5304.110
React:18.2.0
MacOS Monterey:12.3.1
code examples:
App.js
import "./App.css";
import { BrowserRouter, Routes, Route } from "react-router-dom";
import Page1 from "./Page1";
import Page2 from "./Page2";
const App = () => {
return (
<BrowserRouter>
<Routes>
<Route path="/" element={<Page1 />}></Route>
<Route path="test" element={<Page2 />} />
</Routes>
</BrowserRouter>
);
};
export default App;
Page1.js
import { useEffect, useState } from "react";
import { useDispatch, useSelector } from "react-redux";
import { addItem, clearAll, createList, removeItem } from "./testReducer";
const Page1 = () => {
const [isSecondScreenButtonDisable, setIsSecondScreenButtonDisable] =
useState(false);
const dispatch = useDispatch();
const reducerList = useSelector((state) => state.counter);
const array = [1];
window.addEventListener("beforeunload", () => {
localStorage.setItem("isSecondSreenOpen", "false");
localStorage.removeItem("persist:root");
});
window.addEventListener("storage", () => {
const isOpen = localStorage.getItem("isSecondSreenOpen");
if (isOpen === "false") {
setIsSecondScreenButtonDisable(false);
} else {
setIsSecondScreenButtonDisable(true);
}
});
useEffect(() => {
localStorage.setItem("isSecondSreenOpen", true);
window.dispatchEvent(new Event("storage"));
dispatch(createList(array));
}, []);
useEffect(() => {
if (localStorage.getItem("isSecondSreenOpen") === "true") {
window.open("test", "_blank");
}
}, []);
return (
<div className="App">
<button
disabled={isSecondScreenButtonDisable}
onClick={() => {
window.open("test", "_blank");
}}
>
2. sayfa
</button>
<header className="App-header">
<button onClick={() => dispatch(clearAll())}> Hepsini Sil</button>
<button onClick={() => dispatch(addItem(100))}> Ekle</button>
<button onClick={() => dispatch(removeItem())}> Sil</button>
{reducerList.map((item, index) => {
return (
<div key={item + index}>
{index}-{item}
</div>
);
})}
</header>
</div>
);
};
export default Page1;
Page2
import { useEffect } from "react";
import { useSelector } from "react-redux";
const Page2 = () => {
const reducerList = useSelector((state) => state.counter);
const onCloseEvent = () => {
localStorage.setItem("isSecondSreenOpen", false);
window.close();
};
window.addEventListener("beforeunload", () => {
localStorage.setItem("isSecondSreenOpen", false);
});
useEffect(() => {
localStorage.setItem("isSecondSreenOpen", true);
window.addEventListener("storage", () => {
const isOpen = localStorage.getItem("isSecondSreenOpen");
if (isOpen === "false") {
onCloseEvent();
}
});
}, []);
return (
<div className="App">
<header className="App-header">
{reducerList.map((item, index) => {
return (
<div key={item}>
{index}-{item}
</div>
);
})}
</header>
</div>
);
};
export default Page2;
store.js
import { combineReducers, configureStore } from "#reduxjs/toolkit";
import {
persistReducer,
FLUSH,
REHYDRATE,
PAUSE,
PERSIST,
PURGE,
REGISTER,
} from "redux-persist";
import persistStore from "redux-persist/es/persistStore";
import storage from "redux-persist/lib/storage";
import { createStateSyncMiddleware } from "redux-state-sync";
// import secondReducer from "./secondReducer";
import testReducer from "./testReducer";
const persistConfig = {
key: "root",
version: 1,
storage,
// blacklist: ['secondReducer']
};
const rootReducer = combineReducers({
counter: testReducer,
// secondReducer: secondReducer,
});
const persistedReducer = persistReducer(persistConfig, rootReducer);
export const store = configureStore({
reducer: persistedReducer,
middleware: (getDefaultMiddleware) =>
getDefaultMiddleware({
serializableCheck: {
ignoredActions: [FLUSH, REHYDRATE, PAUSE, PERSIST, PURGE, REGISTER],
},
}).concat(
createStateSyncMiddleware({
blacklist: [PERSIST, REHYDRATE],
})
),
});
export const persistor = persistStore(store);
index.js
import React from "react";
import ReactDOM from "react-dom/client";
import "./index.css";
import App from "./App";
import reportWebVitals from "./reportWebVitals";
import { Provider } from "react-redux";
import { persistor, store } from "./store";
import { PersistGate } from "redux-persist/lib/integration/react";
import { initMessageListener } from "redux-state-sync";
const root = ReactDOM.createRoot(document.getElementById("root"));
initMessageListener(store);
root.render(
<Provider store={store}>
<PersistGate loading={null} persistor={persistor}>
<App />
</PersistGate>
</Provider>
);
reportWebVitals();
Edit: Not only my component rerender it starts from index.js
I found the answer it is not about react. I'm using extension called "Awesome Color Picker" when i disable this extension the problem solved.

React Component cant acces Redux store

I have the following modules for my React app:
index.js
import React from 'react';
import ReactDOM from 'react-dom';
import './index.css';
import MainApp from './MainApp';
import reportWebVitals from './reportWebVitals';
ReactDOM.render(
<React.StrictMode>
<MainApp />
</React.StrictMode>,
document.getElementById('root')
);
MainApp.js
import React from 'react';
import App from './containers/app';
import './App.css';
import {ConnectedRouter} from 'connected-react-router'
import {Provider} from 'react-redux';
import {Route, Switch} from 'react-router-dom';
import configureStore, {history} from './store';
export const store = configureStore();
const MainApp = () =>
<Provider store={store}>
<ConnectedRouter history={history}>
<Switch>
<Route path="/" component={App}/>
</Switch>
</ConnectedRouter>
</Provider>;
export default App;
store/index.js
import {applyMiddleware, compose, createStore} from 'redux';
import reducers from '../reducers/index';
import {createBrowserHistory} from 'history'
import {routerMiddleware} from 'connected-react-router';
import createSagaMiddleware from 'redux-saga';
import rootSaga from '../sagas/index';
const history = createBrowserHistory();
const routeMiddleware = routerMiddleware(history);
const sagaMiddleware = createSagaMiddleware();
const middlewares = [sagaMiddleware, routeMiddleware];
const composeEnhancers = window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__ || compose;
export default function configureStore(initialState) {
const store = createStore(reducers(history), initialState,
composeEnhancers(applyMiddleware(...middlewares)));
sagaMiddleware.run(rootSaga);
if (module.hot) {
// Enable Webpack hot module replacement for reducers
module.hot.accept('../reducers/index', () => {
const nextRootReducer = require('../reducers/index');
store.replaceReducer(nextRootReducer);
});
}
return store;
}
export {history};
containers/dataviewer/index.js
import React from 'react';
import {connect} from 'react-redux';
import {
connectPath
} from '../../actions/Paths';
import Button from '#material-ui/core/Button';
import ButtonGroup from '#material-ui/core/ButtonGroup';
import TabularViewer from './TabularViewer';
import CollapsableViewer from './CollapsableViewer';
import ChartViewer from './ChartViewer';
class DataViewer extends React.Component {
constructor() {
super();
this.state = {
displayStyle: null
}
this.handleClick = this.handleClick.bind(this);
}
handleClick = (style) => {
this.setState({displayStyle: style})
}
DisplayControllers = () => {
return (
<ButtonGroup variant="text" color="primary" aria-label="outlined primary button group">
<Button color={this.state.displayStyle === 'tabular'? 'secondary': 'primary'} onClick={() => this.handleClick('tabular')}>Tabular</Button>
<Button color={this.state.displayStyle === 'collapsable'? 'secondary': 'primary'}onClick={() => this.handleClick('collapsable')}>Colapsable</Button>
<Button color={this.state.displayStyle === 'chart'? 'secondary': 'primary'} onClick={() => this.handleClick('chart')}>Gráfico</Button>
</ButtonGroup>
)
}
DisplayComponent = () => {
switch (this.state.displayStyle) {
case 'tabular':
return <TabularViewer />
case 'collapsable':
return <CollapsableViewer />
case 'chart':
return <ChartViewer />
default:
return <p>Seleccione un estilo de desplegado.</p>;
}
}
render() {
return (
<div>
<p>Data Viewer</p>
<this.DisplayControllers />
<this.DisplayComponent />
</div>
)
}
}
const mapStateToProps = ({paths}) => {
const {connection, path, data} = paths;
return {
connection,
path,
data
}
}
export default connect(mapStateToProps, {connectPath})(DataViewer)
Te problem is that I get the following error when the component is mounted:
Error: Could not find "store" in the context of "Connect(DataViewer)". Either wrap the root component in a <Provider>, or pass a custom React context provider to <Provider> and the corresponding React context consumer to Connect(DataViewer) in connect options.
I can't see what I am missing.
EDIT
containers/app.js
import React from 'react';
import DataViewer from './DataViewer';
class App extends React.Component {
render() {
return (
<div className="App">
<header className="App-header">
<DataViewer
database_host="127.0.0.1'"
database_port="5432"
database_name="TCDigital"
database_user="postgres"
database_password="xxxx"
path="Ventas"
displayStyle="collapsable"
/>
</header>
</div>
);
}
}
export default App;

React Router v4 and loadable-components always rendering same Component even though the route matches other

I've got a Standard React-Redux Application with react router v4, webpack 4 and I'm trying to perform lazy loading of components via loadable-components library, so webpack can create chunks and load them on demand. The problems seems it's always rendering the Dashboard (refer code below) Component inside the <Switch>, no matter the route.
I don't understand the cause. I found a similar problem: React-Router v4 rendering wrong component but matching correctly but it follows a different pattern and I can't or don't understand how to apply that solution into my issue.
I'm posting The main AppContainer and the routes file:
AppContainer.js:
import React, { Component } from 'react'
import PropTypes from 'prop-types'
import { Provider } from 'react-redux'
import ReduxToastr from 'react-redux-toastr'
import 'react-redux-toastr/src/styles/index.scss'
import { Offline, Online } from 'react-detect-offline'
import Footer from 'components/Footer/'
import { Modal, ModalHeader, ModalBody } from 'reactstrap'
import { Translate } from 'react-redux-i18n'
import { PersistGate } from 'redux-persist/integration/react'
import { Router, Switch, Route } from 'react-router-dom'
import PrivateRoute from 'components/PrivateRoute'
import { Dashboard, PasswordReset, PasswordResetEdit, SignIn } from 'routes'
// Layout
import CoreLayout from 'layouts/CoreLayout'
class AppContainer extends Component {
static propTypes = {
history: PropTypes.object.isRequired,
persistor: PropTypes.object.isRequired, // redux-persist
store : PropTypes.object.isRequired
}
render () {
const { history, store, persistor } = this.props
const opened = true
const toastrDefaultTimeout = 8000
const newToastrAlwaysOnTop = false
return (
<div>
<Online>
<Provider store={store}>
<PersistGate loading={null} persistor={persistor}>
<div style={{ height: '100%' }}>
<ReduxToastr
timeOut={toastrDefaultTimeout}
newestOnTop={newToastrAlwaysOnTop}
preventDuplicates
position='top-right'
transitionIn='fadeIn'
transitionOut='fadeOut'
progressBar />
<Router history={history}>
<CoreLayout {...this.props} >
<Switch>
<Route exact path='/sign-in' component={SignIn} />
<Route exact path='/passwords/new' component={PasswordReset} />
<Route exact path='/passwords/edit' component={PasswordResetEdit} />
<PrivateRoute exact path='/' component={Dashboard} persistor={persistor} />
</Switch>
</CoreLayout>
</Router>
</div>
</PersistGate>
</Provider>
</Online>
<Offline>
<div className='app'>
<div className='app-body'>
<main className='main align-items-center align-self-center'>
<div className='container'>
<div className='animated fadeIn'>
<Modal isOpen={opened} toggle={this.toggle}>
<ModalHeader toggle={this.toggle}><Translate value={`views.shared.no-internet.title`} /></ModalHeader>
<ModalBody>
<div className='card-block'>
<div className='row'>
<div className='col-sm-12'>
<Translate value='views.shared.no-internet.description' />
</div>
</div>
</div>
</ModalBody>
</Modal>
</div>
</div>
</main>
</div>
<Footer />
</div>
</Offline>
</div>
)
}
}
export default AppContainer
routes/index.js:
// We only need to import the modules necessary for initial render
import loadable from 'loadable-components'
import { hot } from 'react-hot-loader'
// Component split-code (lazy load)
let Dashboard,
SignIn,
PasswordReset,
PasswordResetEdit
// Needed for HMR
if (__DEV__) {
Dashboard = hot(module)(loadable(() => import('./Dashboard')))
PasswordReset = hot(module)(loadable(() => import('./PasswordReset')))
PasswordResetEdit = hot(module)(loadable(() => import('./PasswordResetEdit')))
SignIn = hot(module)(loadable(() => import('./SignIn')))
} else {
Dashboard = loadable(() => import('./Dashboard'))
SignIn = loadable(() => import('./SignIn'))
PasswordReset = loadable(() => import('./PasswordReset'))
PasswordResetEdit = loadable(() => import('./PasswordResetEdit'))
}
export { Dashboard, PasswordReset, PasswordResetEdit, SignIn }
And for anyone curious, here is the PrivateRoute component:
import React, { Component } from 'react'
import PropTypes from 'prop-types'
import { Route, Redirect } from 'react-router-dom'
import { connect } from 'react-redux'
import { validateSession } from 'modules/sessions/session'
const mapStateToProps = state => ({
session: state.session
})
const mapDispatchToProps = (dispatch) => {
return {
validateSession: () => { dispatch(validateSession()) }
}
}
class PrivateRoute extends Component {
componentDidMount () {
this.props.validateSession(this.props.persistor)
}
render () {
const { component: Component, session, ...rest } = this.props
return (
<Route {...rest} render={(routeProps) =>
this.props.session.userSignedIn ? <Component {...routeProps} />
: <Redirect to={{
pathname: '/sign-in',
state: { from: routeProps.location }
}} />
}
/>)
}
}
PrivateRoute.propTypes = {
component: PropTypes.func.isRequired,
persistor: PropTypes.object.isRequired,
session: PropTypes.object.isRequired,
validateSession: PropTypes.func.isRequired
}
export default connect(mapStateToProps, mapDispatchToProps)(PrivateRoute)
If I remove the if (_DEV_) block in routes/index.js and always do it like the 'else' block everything works okay, however I lose HMR, meaning I have to refresh the browser to see code changes take effect.
Edit
For anyone wandering where the history prop comes from:
Main app entry point (in webpack.config.js). src/main.js:
import React from 'react'
import ReactDOM from 'react-dom'
import createStore from './store/createStore'
import { hot } from 'react-hot-loader'
import AppContainer from './containers/AppContainer'
import { persistStore } from 'redux-persist'
// ========================================================
// Store Instantiation
// ========================================================
const initialState = window.___INITIAL_STATE__
const { history, store } = createStore(initialState)
// begin periodically persisting the store
let persistor = persistStore(store)
// ========================================================
// Render Setup
// ========================================================
const MOUNT_NODE = document.getElementById('root')
let render = () => {
ReactDOM.render(
<AppContainer store={store} persistor={persistor} history={history} />,
MOUNT_NODE
)
}
// This code is excluded from production bundle
if (__DEV__) {
if (module.hot) {
// Development render functions
const renderApp = render
const renderError = (error) => {
const RedBox = require('redbox-react').default
ReactDOM.render(<RedBox error={error} />, MOUNT_NODE)
}
// Wrap render in try/catch
render = () => {
try {
renderApp()
} catch (error) {
console.error(error)
renderError(error)
}
}
// Setup hot module replacement
module.hot.accept('./containers/AppContainer', () =>
render(require('./containers/AppContainer').default)
)
}
}
// ========================================================
// Go!
// ========================================================
export default hot(module)(render)
render()
createStore.js:
import { applyMiddleware, compose, createStore } from 'redux'
import thunk from 'redux-thunk'
import { apiMiddleware } from 'redux-api-middleware'
import { createLogger } from 'redux-logger'
import makeRootReducer from './reducers'
import { routerMiddleware } from 'react-router-redux'
// I18n
import { syncTranslationWithStore, loadTranslations, setLocale } from 'react-redux-i18n'
import { translationsObject } from 'translations/index'
// Router history
import createHistory from 'history/createBrowserHistory'
// Raven for Sentry
import Raven from 'raven-js'
import createRavenMiddleware from 'raven-for-redux'
export default (initialState = {}) => {
// ======================================================
// Middleware Configuration
// ======================================================
const logger = createLogger()
const history = createHistory()
const historyMiddleware = routerMiddleware(history)
const middleware = [historyMiddleware, apiMiddleware, thunk, logger]
if (__PROD__) {
Raven.config(`${__SENTRY_DSN__}`).install()
middleware.push(createRavenMiddleware(Raven))
}
// ======================================================
// Store Enhancers
// ======================================================
const enhancers = []
let composeEnhancers = compose
const composeWithDevToolsExtension = window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__
if (typeof composeWithDevToolsExtension === 'function') {
composeEnhancers = composeWithDevToolsExtension
}
// ======================================================
// Store Instantiation and HMR Setup
// ======================================================
const store = createStore(
makeRootReducer(),
initialState,
composeEnhancers(
applyMiddleware(...middleware),
...enhancers
)
)
store.asyncReducers = {}
// DEPRECATED in react-router v4: To unsubscribe, invoke `store.unsubscribeHistory()` anytime
// store.unsubscribeHistory = browserHistory.listen(updateLocation(store))
if (module.hot) {
const reducers = require('./reducers').default
module.hot.accept('./reducers', () => {
store.replaceReducer(reducers(store.asyncReducers))
})
}
syncTranslationWithStore(store)
store.dispatch(loadTranslations(translationsObject))
store.dispatch(setLocale('es_AR'))
return { history, store }
}

admin-on-rest custom template getCrudList

I try to build a custom template. I follow the instructions for the App.js from the page https://marmelab.com/admin-on-rest//CustomApp.html.
I connect my component PostList to react-redux. I see the request in the inspector but the store is still empty.
I don't know what it miss to do for working fine.
App.js
import React from 'react';
// redux, react-router, redux-form, saga, and material-ui
// form the 'kernel' on which admin-on-rest runs
import { combineReducers, createStore, compose, applyMiddleware } from 'redux';
import { Provider } from 'react-redux';
import createHistory from 'history/createBrowserHistory';
import { Switch, Route } from 'react-router-dom';
import { ConnectedRouter, routerReducer, routerMiddleware } from 'react-router-redux';
import { reducer as formReducer } from 'redux-form';
import createSagaMiddleware from 'redux-saga';
import MuiThemeProvider from 'material-ui/styles/MuiThemeProvider';
import AppBar from 'material-ui/AppBar';
// prebuilt admin-on-rest features
import {
adminReducer,
localeReducer,
crudSaga,
jsonServerRestClient,
TranslationProvider,
} from 'admin-on-rest';
// your app components
import PostList from './posts';
// your app labels
const messages = {
en: {
'main.heading': 'retranslate #{{ versionNumber }}',
'main.subtitle': 'Real simple translations for react.',
'current.language': 'Your current language is {{ currentLanguage }}',
'bold.thing': 'This <b>text</b> is bold',
},
et: {
'main.heading': 'retranslate #{{ versionNumber }}',
'main.subtitle': 'Väga lihtsad tõlked reactile.',
'current.language': 'Teie hetke keel on {{ language }}',
'bold.thing': 'See <b>tekst</b> on tumedam',
},
};
// create a Redux app
const reducer = combineReducers({
admin: adminReducer,
locale: localeReducer(),
form: formReducer,
routing: routerReducer,
});
const sagaMiddleware = createSagaMiddleware();
const history = createHistory();
const store = createStore(reducer, undefined, compose(
applyMiddleware(sagaMiddleware, routerMiddleware(history)),
window.devToolsExtension ? window.devToolsExtension() : f => f,
));
const restClient = jsonServerRestClient('http://jsonplaceholder.typicode.com');
sagaMiddleware.run(crudSaga(restClient));
const Dash = () => {
return <div>Dash</div>;
};
// bootstrap redux and the routes
const App = () => (
<Provider store={store}>
<TranslationProvider messages={messages}>
<ConnectedRouter history={history}>
<MuiThemeProvider>
<div>
<AppBar title="My Admin" />
<Switch>
<Route exact path="/" render={(routeProps) => <Dash {...routeProps} />} />
<Route exact path="/posts" hasCreate render={(routeProps) => <PostList resource="posts" {...routeProps} />} />
</Switch>
</div>
</MuiThemeProvider>
</ConnectedRouter>
</TranslationProvider>
</Provider>
);
export default App;
posts.js
import React, { Component } from 'react';
import { connect } from 'react-redux';
import { crudGetList as crudGetListAction} from 'admin-on-rest';
class PostList extends Component {
componentWillMount() {
this.props.crudGetList('posts', {page: 1, parPage: 10}, {field: 'id', order: 'ASC'});
}
render() {
return <div>Posts</div>
}
}
function mapStateToProps(state) {
console.log(state.admin.resources);
return {
list: []
}
}
export default connect(mapStateToProps, {
crudGetList: crudGetListAction
})(PostList)
Our documentation has been updated today. You're missing the following line:
store.dispatch(declareResources([{ name: 'posts' }]));
You can insert it just before declaring your restClient

New to react and Redux, why am i getting an undefined store being passed?

I am not sure if I am even setting up this redux-react project correctly. I am confused as to how I can actually start using store within my react app.
When I try to console.log store I am getting undefined. I have gotten most of this from a boilerplate and am unsure of how some of these parts interact. Currently I have an index.js with
import { Provider } from 'react-redux'
import { configureStore } from './store/configureStore';
const store = configureStore()
import { Root} from './containers/Root';
import Home from './containers/Home'
ReactDOM.render(
<Provider store={store}>
<Router history={browserHistory}>
<Route path="/" component={Root}>
<IndexRoute component={Home} />
</Route>
</Router>
</Provider>,
document.getElementById('root')
);
Root.js :
import React, { Component } from 'react';
import DevTools from './DevTools';
import MyNavbar from '../components/MyNavbar';
import Footer from '../components/Footer'
module.exports = class Root extends Component {
render() {
const { store } = this.props;
console.log(store)
return (
<div>
<MyNavbar />
{this.props.children}
<Footer />
{/* Being the dev version of our Root component, we include DevTools below */}
{/*<DevTools />*/}
</div>
);
}
};
Home component:
import React, { Component, PropTypes } from 'react';
import { Row, Col, Grid } from 'react-bootstrap'
import HowItWorks from '../components/HowItWorks'
import GetStarted from '../components/GetStarted'
import Setup from './Setup'
export default class Home extends Component {
render() {
// we can use ES6's object destructuring to effectively 'unpack' our props
return (
<section>
<div className="slider-wrapper">
<GetStarted />
</div>
<Grid>
<div className="howwork-wrapper">
<Row >
<Col md={12}>
<HowItWorks />
</Col>
</Row>
</div>
</Grid>
</section>
);
}
}
configureStore.js :
import { createStore, applyMiddleware, compose } from 'redux';
import rootReducer from '../reducers';
import createLogger from 'redux-logger';
import thunk from 'redux-thunk';
import DevTools from '../containers/DevTools';
const logger = createLogger();
const finalCreateStore = compose(
applyMiddleware(logger, thunk),
DevTools.instrument()
)(createStore);
module.exports = function configureStore(initialState) {
const store = finalCreateStore(rootReducer, initialState);
if (module.hot) {
module.hot.accept('../reducers', () =>
store.replaceReducer(require('../reducers'))
);
}
return store;
};
reducers/index.js:
import { combineReducers } from 'redux';
import auth from './auth'
const rootReducer = combineReducers({
auth
});
export default rootReducer;
reducers/auth.js:
import { LOGIN, LOGIN_FAIL, LOGOUT } from '../constants/ActionTypes'
export default function auth(state = {}, action) {
switch (action.type) {
case LOGIN:
return state;
case LOGIN_FAIL:
return state ;
case LOGOUT:
return state ;
default:
return state;
}
}
constants/ActionTypes:
export const LOGIN = 'LOGIN';
export const LOGIN_FAIL = 'LOGIN_FAIL';
export const LOGOUT = 'LOGOUT';
You need to connect your components to get access to the store/state. To do this, modify your Root component like this:
import React, { Component } from 'react';
import { connect } from 'react-redux';
import DevTools from './DevTools';
import MyNavbar from '../components/MyNavbar';
import Footer from '../components/Footer'
class Root extends Component {
render() {
const { state } = this.props;
console.log(state)
return (
<div>
<MyNavbar />
{this.props.children}
<Footer />
{/* Being the dev version of our Root component, we include DevTools below */}
{/*<DevTools />*/}
</div>
);
}
};
const mapStateToProps = (state) => {
return {
state: state
}
}
module.exports = connect(mapStateToProps)(Root);
A few notes, since you are transpiling anyway, you could export instead of module.exports in your declaration. Also, generally you do not want to expose your entire state to a single component. You can connect multiple components (make them "containers") by following this pattern.
The following is an example component connected to your state.
import React, { Component } from 'react';
import { connect } from 'react-redux';
export class SomeComponent extends Component {
render() {
const { someKey, dispatchSomething } = this.props;
return (
<div onClick={dispatchSomething}>
<h1>My rendered someKey variable: {someKey}</h1>
</div>
);
}
};
const mapStateToProps = (state) => {
return {
someKey: state.someReducer.someKey
}
}
const mapDispatchToProps = (dispatch) => {
return {
dispatchSomething: () => dispatch(someActionCreator())
}
}
export default connect(mapStateToProps, mapDispatchToProps)(SomeComponent);
References
react-redux API: connect

Resources