React Admin | Uncaught Error: Missing history prop - javascript

I am trying to implement react-admin in my project however upon render I get this message
Uncaught Error: Missing history prop. When integrating react-admin inside an existing redux Provider, you must provide the same 'history' prop to the <Admin> as the one used to bootstrap your routerMiddleware. React-admin uses this history for its own ConnectedRouter.
There is very little to be found about this issue and I'm not entirely sure how to go about setting the history. I've tried importing createHistory from 'history/createHashHistory' but then I get this error
Uncaught Could not find router reducer in state tree, it must be mounted under "router"
Is there a way to get this rendering properly? And if so, what would be the proper way to go about configuring the Admin Component's history?
index.js
import React from 'react';
import ReactDOM from 'react-dom';
import './index.css';
import App from './App';
import { createStore, compose, applyMiddleware} from 'redux';
import { Provider } from 'react-redux';
import thunk from 'redux-thunk';
import { BrowserRouter as Router } from 'react-router-dom';
import { reducer } from './redux/reducer';
import * as serviceWorker from './serviceWorker';
const store = createStore(reducer, compose(
applyMiddleware(thunk),
window.navigator.userAgent.includes('Chrome') ?
window.__REDUX_DEVTOOLS_EXTENSION__ && window.__REDUX_DEVTOOLS_EXTENSION__() : compose,
),
);
ReactDOM.render(
<React.StrictMode>
<Router>
<Provider store={store}>
<App />
</Provider>
</Router>
</React.StrictMode>,
document.getElementById('root')
);
AdminPage.js
import { Admin, Resource } from 'react-admin';
import jsonServerProvider from 'ra-data-json-server';
const dataProvider = jsonServerProvider('http://localhost:3000');
const AdminPage = () => {
return (
<Admin dataProvider={dataProvider}>
<Resource name="services" />
</Admin>
)
}
export default AdminPage;
App.js
import React from 'react';
import './App.css';
import AdminPage from './components/AdminPage';
import { Switch, Route } from 'react-router-dom';
const App = () => {
return (
<div className="app">
<Switch>
<Route exact path='/manage' component={ AdminPage } />
</Switch>
</div>
);
}
export default App;

The proper way to write a reducer in this case is documented at https://marmelab.com/react-admin/CustomApp.html#using-an-existing-redux-provider.
But from what I see, you don't need one. You just added the thunk middleware, which is useless in react-admin as it already handles sagas.

Related

React App showing blank page after deploy

my react app works fine on my pc using google chrome (where I build it) but doesn't work on Microsoft edge, and also works fine in localhost, but on my laptop and phone it shows a blank page, my host is: http://dorian1.com/
i tried using hashrouter instead of browserrouter, adding "homepage" in my package.json,
my app.js is :
import React, { useState } from 'react'
import { BrowserRouter as Router, Switch, Route} from 'react-router-dom'
import './styles/App.css'
import Footer from './footer/Footer'
import Header from './header/Header'
import Search from './search/Search'
import Alert from './alert/Alert'
// PAGES
import News from './pages/news/News'
import Opinion from './pages/opinion/Opinion'
import Sport from './pages/sport/Sport'
import Culture from './pages/culture/Culture'
import Lifestyle from './pages/lifestyle/Lifestyle'
import SearchResults from './pages/searchResults/SearchResults'
import SingleArticle from './pages/SingleArticle/SingleArticle'
function App() {
const [alert, setAlert] = useState(null)
return (
<Router>
<Header />
<Alert alert={alert}/>
<Search setAlert={setAlert}/>
<Switch>
<Route exact path='/' component={News}/>
<Route path='/News' component={News}/>
<Route path='/Opinion' component={Opinion}/>
<Route path='/Sport' component={Sport}/>
<Route path='/Culture' component={Culture}/>
<Route path='/Lifestyle' component={Lifestyle}/>
<Route path='/SearchResults' component={SearchResults}/>
<Route path='/SingleArticle' component={SingleArticle}/>
</Switch>
<Footer />
</Router>
);
}
export default App;
the console on edge says :
Uncaught TypeError: Cannot read properties of undefined (reading 'apply')
at redux.js:621
at f (redux.js:154)
at Module.454 (store.js:11)
at a ((index):1)
at t ((index):1)
at Array.r [as push] ((index):1)
at main.a2301ff4.chunk.js:1
my index.js is :
import React from 'react';
import ReactDOM from 'react-dom';
import App from './App';
// REDUX
import { Provider } from 'react-redux'
// import store from './store'
import { PersistGate } from 'redux-persist/integration/react'
import {store, persistor} from './store'
ReactDOM.render(
<Provider store={store}>
<PersistGate persistor={persistor}>
<App />
</PersistGate>
</Provider>,
document.getElementById('root')
);
i fixed it, my store js had thunk middleware imported but never used,
const middleware = [thunk];
export const store = createStore(rootReducer,
initialState,
compose(
applyMiddleware(...middleware),
window.__REDUX_DEVTOOLS_EXTENSION__ && window.__REDUX_DEVTOOLS_EXTENSION__()
));
i removed the middleware and now its working , makes no sense but hope it helps someone

What does it meant - Browser history needs a DOM? [duplicate]

I am trying server side rendering using react-router 4. I am following the example provided here https://reacttraining.com/react-router/web/guides/server-rendering/putting-it-all-together
As per the example on server we should use StaticRouter. When I import as per the example I am seeing StaticRouter as undefined
import {StaticRouter} from 'react-router';
After doing some research online I found I could use react-router-dom. Now my import statement looks like this.
import {StaticRouter} from 'react-router-dom';
However when I run the code I am getting Invariant Violation: Browser history needs a DOM in the browser.
my server.js file code
....
app.get( '*', ( req, res ) => {
const html = fs.readFileSync(path.resolve(__dirname, '../index.html')).toString();
const context = {};
const markup = ReactDOMServer.renderToString(
<StaticRouter location={req.url} context={context} >
<App/>
</StaticRouter>
);
if (context.url) {
res.writeHead(302, {
Location: context.url
})
res.end();
} else {
res.send(html.replace('$react', markup));
}
} );
....
And my client/index.js code
....
ReactDOM.render((
<BrowserRouter>
<App />
</BrowserRouter>
), root);
....
Update v1
Reduced my example to a bear minimum and still getting the same error.
clientIndex.js
import ReactDOM from 'react-dom'
import { BrowserRouter } from 'react-router-dom'
import App from '../App'
ReactDOM.render((
<BrowserRouter>
<App/>
</BrowserRouter>
), document.getElementById('app'))
serverIndex.js
import { createServer } from 'http'
import React from 'react'
import ReactDOMServer from 'react-dom/server'
import { StaticRouter } from 'react-router'
import App from '../App'
createServer((req, res) => {
const context = {}
const html = ReactDOMServer.renderToString(
<StaticRouter
location={req.url}
context={context}
>
<App/>
</StaticRouter>
)
res.write(`
<!doctype html>
<div id="app">${html}</div>
`)
res.end()
}).listen(3000);
App.js
import React from 'react';
import { BrowserRouter as Router, Route } from 'react-router-dom';
import routes from "./client/routes";
const App = ( ) => (
<Router>
<Route path="/" exact render={( props ) => ( <div>Helloworld</div> )} />
</Router>
)
export default App;
You need to use different history provider for server side rendering because you don't have a real DOM (and browser's history) on server. So replacing BrowserRouter with Router and an alternate history provider in your app.js can resolve the issue. Also you don't have to use two wrappers. You are using BrowserRouter twice, in app.js as well as clientIndex.js which is unnecessary.
import { Route, Router } from 'react-router-dom';
import { createMemoryHistory } from 'history';
const history = createMemoryHistory();
<Router history={history}>
<Route path="/" exact render={( props ) => ( <div>Helloworld</div> )} />
</Router>
You can now replace StaticRouter with ConnectedRouter which can be used both in client and server. I use the following code to choose between history and export it to be used in ConnectedRouter's history.
export default (url = '/') => {
// Create a history depending on the environment
const history = isServer
? createMemoryHistory({
initialEntries: [url]
})
: createBrowserHistory();
}
In clientIndex.js
Rather than BrowserRouter use StaticRouter.
import { BrowserRouter } from 'react-router-dom';
import { StaticRouter } from 'react-router-dom'
As is essentially noted in the comments, one may hit this error (as I have) by accidentally wrapping your App component in a <BrowserRouter>, when instead it is your client app that should be wrapped.
App.js
import React from 'react'
const App = () => <h1>Hello, World.</h1>
export default App
ClientApp.js
import React from 'react'
import { BrowserRouter } from 'react-router-dom'
import ReactDOM from 'react-dom'
import App from './App'
const render = Component => {
ReactDOM.render(
<BrowserRouter>
<Component />
</BrowserRouter>,
document.getElementById('app')
)
}
render(App)
See also the React Router docs.

Different Problem: React store.getState is not a function

Here is my code:
store.js
import { createStore, applyMiddleware, compose } from "redux";
import thunk from "redux-thunk";
import rootReducer from "./reducers";
const initialState = {};
const middleware = [thunk];
const store = createStore(
rootReducer,
initialState,
compose(
applyMiddleware(...middleware),
(window.__REDUX_DEVTOOLS_EXTENSION__ &&
window.__REDUX_DEVTOOLS_EXTENSION__()) ||
compose
)
);
App.js
import React, { Component } from 'react';
import './sass/main.scss';
import { BrowserRouter as Router, Route } from "react-router-dom";
import Landing from './components/pages/Landing';
import { Provider } from "react-redux";
import store from "../src/store";
import Register from "./components/auth/Register";
import Login from "./components/auth/Login";
class App extends Component {
render() {
return (
<Provider store={store}>
<Router>
<div className="App">
<Route exact path='/' component={Landing}/>
<Route exact path="/login" component={Login}/>
<Route exact path="/register" component={Register}/>
</div>
</Router>
</Provider>
);
}
}
export default App;
I can't find where the problem is. I tried to debug it, but can't found what really make it those error. error: Uncaught TypeError: store.getState is not a function.
./src/App.js
Attempted import error: '../src/store' does not contain a default export (imported as 'store').
The first thing tried is adding a export default createStore but that bring up other error message saying Line 10: 'store' is assigned a value but never used and TypeError: store.getState is not a function
In your App.js you are trying to import store from "../src/store";. So system will try to import something from ../src/store.js but you never export any variable in that file.
You can update store.js to add export statement
const store = createStore(
rootReducer,
initialState,
compose(
applyMiddleware(...middleware),
(window.__REDUX_DEVTOOLS_EXTENSION__ &&
window.__REDUX_DEVTOOLS_EXTENSION__()) ||
compose
)
);
export default store;

React Router Redux upgrade from v3 to v4

I am trying to upgrade react-router from v3 to v4 and getting the following error when running the application :
Uncaught Error: Element type is invalid: expected a string (for built-in components) or a class/function (for composite components) but got: undefined. You likely forgot to export your component from the file it's defined in.
reducer :
import { combineReducers } from 'redux';
import { routerReducer } from 'react-router-redux';
import { login } from './login-reducer';
const reducers = combineReducers({
routing: routerReducer,
login
})
export default reducers;
store :
import { createStore, applyMiddleware, compose } from 'redux'
import thunk from 'redux-thunk';
import createHistory from 'history/createBrowserHistory'
import { routerMiddleware } from 'react-router-redux'
import reducers from '../reducers';
export const history = createHistory();
const routerMiddle = routerMiddleware(history);
const composeEnhancers =
typeof window === 'object' &&
window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__ ?
window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__({
// Specify extension’s options like name, actionsBlacklist, actionsCreators, serialize...
}) : compose;
let middleware = [routerMiddle, thunk]
const enhancer = composeEnhancers(
applyMiddleware(...middleware),
// other store enhancers if any
);
const store = createStore(reducers, enhancer);
export default store;
index :
import React from 'react';
import ReactDOM from 'react-dom';
import { Provider } from 'react-redux'
import store, { history } from './store/createStore';
import { ConnectedRouter } from 'react-router-redux'
import { getRoutes } from './routes';
import './index.css';
ReactDOM.render(
<Provider store={store}>
<ConnectedRouter history={history}>
<div>{ getRoutes(store) }</div>
</ConnectedRouter>
</Provider>,
document.getElementById('root')
);
routes :
import React from 'react';
import { Route } from 'react-router';
import App from './containers/App';
import Login from './containers/Login';
import Protected from './components/Protected';
export const getRoutes = (store) => {
const authRequired = (nextState, replaceState) => {
// Now you can access the store object here.
const state = store.getState();
if (!state.login.loggedIn || state.login.loggedIn == null) {
// Not authenticated, redirect to login.
replaceState({
pathname: '/login',
state: { nextPathname: nextState.location.pathname }
});
}
};
return (
<div>
<Route exact path="/" component={App} />
<Route path="/login" component={Login} />
<Route path="/protected" component={Protected} onEnter={authRequired} />
</div>
);
}
app :
import React, { Component } from 'react';
import { Link } from 'react-router';
import { connect } from 'react-redux'
import logo from '../logo.svg';
import './App.css';
class App extends Component {
render() {
const isLoggedIn = this.props.isLoggedIn;
return (
<div className="App">
<div className="App-header">
<img src={logo} className="App-logo" alt="logo" />
<h2><Link to="/">Welcome to React</Link></h2>
<div className="app-nav">
<nav>
<Link to='about'>About</Link>
<Link to='login'>{( isLoggedIn ? 'Logout' : 'Login' )}</Link>
<Link to='protected'>Protected</Link>
</nav>
</div>
</div>
{this.props.children}
</div>
);
}
}
const mapStateToProps = (state) => {
return {
isLoggedIn: (state.login.loggedIn ? state.login.loggedIn : false)
}
}
App = connect (
mapStateToProps
)(App)
export default App;
Not sure what I'm missing to get this to work
**NOTE : I am still seeing this error even when I only have one route / and render a static component (single div with text)
A) You dont need react router redux at all with react router 4, specially if all you want is to render authenticated components.
B) The onEnter hook dont work this way in RR4, its the older way of doing route validation
{this.props.children} why are you still rendering child routes using props.children? All the child routes goes into the component it resides in.
If you are trying to learn rr4, i recommend checking this boilerplate for it https://github.com/touqeerkhan11/The-Ultimate-Boilerplate
The problem is probably here
import { Link } from 'react-router';
react-router doesn't export Link, react-router-dom does.
import { Link } from 'react-router-dom';
You should probably import the Route component from react-router-dom as well.
The issue had to do with the version of react-router-redux that was being used. When I added it to the project, I left off the #next

New version react router doesn't work with redux

maybe this issue is because the new version of the react router have few days, but I have been reading about this issue and I want to clarify what is going on. I am using the last version of the react router and I want to do the routing through redux. I follow the steps that are listed in the documentation of the redux router module: https://github.com/ReactTraining/react-router/tree/master/packages/react-router-redux, but I receive this error when I make the implementation: (I know that the issue is in the server render)
Invariant Violation: Browser history needs a DOM
Here is my code (the important parts):
server.js
import { Provider } from 'react-redux';
import store from './store';
lisaApp.get('*', function (req, res) {
const context = {};
const html = renderToString(
<Provider store={store}>
<MuiThemeProvider muiTheme={getMuiTheme()}>
<StaticRouter location={req.url} context={context}>
<Routes />
</StaticRouter>
</MuiThemeProvider>
</Provider>,
);
res.setHeader('Content-Type', 'text/html');
if (context.url) {
res.writeHead(301, {
Location: context.url,
});
res.end();
}
res.write(
renderToStaticMarkup(<Layout title={req.title} content={html} />),
);
res.end();
}
client.js
import { Provider } from 'react-redux';
import createHistory from 'history/createBrowserHistory';
import { BrowserRouter } from 'react-router-dom';
import store from './store';
render((
<Provider store={store}>
<MuiThemeProvider muiTheme={getMuiTheme()}>
<BrowserRouter history={createHistory()}>
<Routes />
</BrowserRouter>
</MuiThemeProvider>
</Provider>),
document.getElementById('app'));
store.js
import { createStore, combineReducers, applyMiddleware } from 'redux'
import createHistory from 'history/createBrowserHistory'
import { ConnectedRouter, routerReducer, routerMiddleware, push } from 'react-router-redux'
import thunk from 'redux-thunk';
import reducer from './reducer';
const history = createHistory()
const middlewareHistory = routerMiddleware(history)
const store = createStore(
combineReducers({
reducer,
router: routerReducer
}),
applyMiddleware(
middlewareHistory,
thunk
)
);
export default store;
component.js (dispatch)
const mapDispatchToProps = dispatch => {
return {
onNavigateTo(dest) {
dispatch(push(dest));
}
};
};
Obviously the dispatch, from my component never is called. Anyone can me clarify me where I am wrong? or maybe this feature is not implemented yet in the react router redux module? In advance Thanks.
Instead of BrowserRouter, use ConnectedRouter from the react-router-redux library:
import { Provider } from 'react-redux';
import createHistory from 'history/createBrowserHistory';
import { ConnectedRouter } from 'react-router-redux';
import store from './store';
render((
<Provider store={store}>
<MuiThemeProvider muiTheme={getMuiTheme()}>
<ConnectedRouter history={createHistory()}>
<Routes />
</ConnectedRouter>
</MuiThemeProvider>
</Provider>),
document.getElementById('app'));
I faced the same issue few days ago. In your BrowserRouter you manually create and pass a browserHistory object. Then, whenever you need access to the history object you import it, like it happens in your store.js, which is shared between the server and the client. However, on the server there is no DOM, hence the error.
I fixed the issue by NOT creating the history object manually, this is not needed since you use the BrowserRouter. As stated in the documentation, the BrowserRouter is:
A <Router> that uses the HTML5 history API (pushState, replaceState and the popstate event) to keep your UI in sync with the URL.
Instead of importing the history manually whenever you need it, just use the handy withRouter higher order component. This way, in your props you will get access not only to the history, but also to the closest <Routes>'s match. Read more about it here.
Regarding your last point about the dispatch, you are right that it's not called. Whenever you create a component via the connect of react-redux, remember to wrap it with the withRouter higher order component. Read more about it here.
I believe the above will fix your issue. I can share a working example if you want me to, but my solution is very similar to what you have, with the exception of my above comments.
I hope this helps.
This has worked for me.
I used ConnectRouter from the "react-router-dom" library. Along with it i used BrowserRouter from the "react-router-dom" library. **Now i am able to use React+Redux+Routing happily.
import React from "react";
import ReactDOM from "react-dom";
import { BrowserRouter } from "react-router-dom";
import { Provider } from "react-redux";
import { createStore } from "redux";
import rootReducer from "./reducers";
import App from "./App";
import { connectRouter } from "connected-react-router";
import createHistory from "history/createBrowserHistory";
export const history = createHistory();
const store = createStore(connectRouter(history)(rootReducer));
ReactDOM.render(
<Provider store={store}>
<BrowserRouter>
<App />
</BrowserRouter>
</Provider>,
document.getElementById("root")
);
I hope this will work for you as well.
Happy coding!!!

Categories

Resources