I'm working on a Universal React project, my client entry point is:
import React from 'react'
import {render} from 'react-dom'
import {Provider} from 'react-redux'
import {AppContainer} from 'react-hot-loader'
import {Router, browserHistory} from 'react-router'
import {syncHistoryWithStore} from 'react-router-redux'
import {addLocaleData} from 'react-intl'
import it from 'react-intl/locale-data/it'
import en from 'react-intl/locale-data/en'
import IntlProvider from 'shared/containers/IntlProvider'
import configureStore from 'shared/configureStore'
import routes from 'shared/routes'
import {isDev, isLive} from 'shared/config'
[en, it].forEach(addLocaleData)
const hook = document.getElementById('app')
const initialState = JSON.parse(hook.getAttribute('data-initial-state'))
const store = configureStore(initialState)
const history = syncHistoryWithStore(browserHistory, store)
let content = (
<Provider store={store}>
<IntlProvider key="intl">
<Router history={history}>
{routes}
</Router>
</IntlProvider>
</Provider>
)
if (isLive) {
content = <AppContainer>{content}</AppContainer>
}
function renderApp() {
render(content, hook)
}
if (isLive) {
module.hot.accept('./index.js')
module.hot.accept('../shared/routes', renderApp)
}
renderApp()
On component changes, the reload seems to work, but no render is applied..
maybe it happens before the hot reload trick happens?
NOTE my routes configuration is classic non dymanic routes for now.
I had the same problem because I just forgot to add code for modules replacing
if (module.hot) {
module.hot.accept(
"./App",
() => {
const NextApp = require("./App").App; // THIS LINE
render(NextApp);
},
);
}
Related
I'm pretty confident this is due to the RTL render not being used, but I'm not sure where the render should go given I apparently have to use ReactDOM.react. At least it was needed to resolve one 4-5 errors I got along the way.
App and test code...
// App.jsx
import React from 'react';
import { Provider } from 'react-redux';
import { compose } from 'redux';
import { Switch, Route } from 'react-router-dom';
import { ConnectedRouter } from 'connected-react-router';
import Authentication from './Authentication';
import configureStore, { history } from '../services/history';
const App = () => {
const protectedRoute = compose(Timers, RequireAuth);
const store = configureStore();
return (
<div data-testid='App'>
<Provider store={store}>
<ConnectedRouter history={history}>
<Switch>
<Route exact path='/' component={Authentication} />
</Switch>
</div>
</ConnectedRouter>
</Provider>
</div>
);
};
export default App;
// Authentication.jsx
import React, { Component } from 'react';
export default class Authentication extends Component {
render() {
return (
<div data-testid='Authentication' id='auth'>
...
</div>
);
}
}
Started out with this test:
// Authentication.test.jsx
import React from 'react';
import { render, screen } from '#testing-library/react';
import Authentication from './Authentication';
test('authentication page renders', () => {
render(<Authentication />);
const auth = screen.getByTestId('Authentication');
expect(auth).toBeInTheDocument();
});
Which resulted in:
Error: Uncaught [Error: Could not find "store" in the context of "Connect(Form(Connect(Signin)))". 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(Form(Connect(Signin))) in connect options.]
Addressing this issue, and 4-5 other subsequent errors, has resulted in the following:
import React from 'react';
import ReactDOM from 'react-dom';
import { Provider } from 'react-redux';
import { render, screen } from '#testing-library/react';
import '#testing-library/jest-dom';
import thunk from 'redux-thunk';
import configureMockStore from 'redux-mock-store';
import Authentication from './Authentication';
import { MemoryRouter } from 'react-router-dom';
const middlewares = [thunk];
const mockStore = configureMockStore(middlewares);
const store = mockStore({
auth: {
authenticated: true,
},
});
test('authentication page renders', () => {
const div = document.createElement('div');
ReactDOM.render(
<Provider store={store}>
<MemoryRouter>
<Authentication />
</MemoryRouter>
</Provider>,
div
);
const authentication = screen.queryByTestId('Authentication');
expect(authentication).toBeInTheDocument();
});
Which results in:
received value must be an HTMLElement or an SVGElement.
Received has value: null
28 | );
29 | const authentication = screen.queryByTestId('Authentication');
> 30 | expect(authentication).toBeInTheDocument();
| ^
31 | });
32 |
Again, pretty sure this is from not using RTLs render. Not sure where it should go and adding it creates the first error I had so I'm going in circles.
Any suggestions?
If it is helpful I could retrace all my previous steps and post the various error messages which led me to this point.
Well, somewhere along the line of addressing the various error messages that came up, one fix was adding ReactDOM.render()... thought it was necessary, turns out it was not.
This is sufficient and works:
import React from 'react';
import { Provider } from 'react-redux';
import { render, screen } from '#testing-library/react';
import thunk from 'redux-thunk';
import configureMockStore from 'redux-mock-store';
import { MemoryRouter } from 'react-router-dom';
import '#testing-library/jest-dom';
import Authentication from './Authentication';
const middlewares = [thunk];
const mockStore = configureMockStore(middlewares);
const store = mockStore({
auth: {
authenticated: true,
},
});
test('authentication page renders', () => {
render(
<Provider store={store}>
<MemoryRouter>
<Authentication />
</MemoryRouter>
</Provider>
);
const authentication = screen.getByTestId('Authentication');
expect(authentication).toBeInTheDocument();
});
This is my index.js code
import React from "react";
import { render } from "react-dom";
import "./index.css";
import App from "./App";
import * as serviceWorker from "./serviceWorker";
import { BrowserRouter } from "react-router-dom";
import "#fortawesome/fontawesome-free/css/all.min.css";
import "bootstrap-css-only/css/bootstrap.min.css";
import "mdbreact/dist/css/mdb.css";
import { createStore } from "redux";
import { userReducer } from "../src/reducers/UserReducer";
import { Provider } from "react-redux";
import { loadState, saveState } from "./store/LocalStorage";
const persistedState = loadState();
let store = createStore(userReducer, persistedState);
store.subscribe(() => saveState(store.getState()));
render(
<Provider store={store}>
<BrowserRouter>
<App />
</BrowserRouter>
</Provider>,
document.getElementById("root")
);
serviceWorker.unregister();
This is the error I am getting on browser
"Error: You cannot render a <Router> inside another <Router>. You should never have more than one in your app."
Please help me with some solution.
I guess you are using React Router 6. They dropped support for nested Routers(like MemoryRouter inside of BrowserRouter or any other combinations)
https://github.com/remix-run/react-router/issues/7375
There is rather a hack with <UNSAFE_LocationContext.Provider but as name suggests it's not guaranteed this will work for any(even patch version) updates.
I suggest you either going back to V5 or getting rid of nesting.
This is my code:
import 'core-js';
import 'react-app-polyfill/ie9';
import 'react-app-polyfill/stable';
import 'react-app-polyfill/ie11';
import React from 'react';
import ReactDOM from 'react-dom';
import { Provider } from 'react-redux';
import { AppContainer } from 'react-hot-loader';
import { createBrowserHistory } from 'history';
import configureStore from 'src/store/configureStore';
import createRootReducer from 'src/store/rootReducer';
import App from './Main/App';
import './index.css';
const history = createBrowserHistory();
const { store } = configureStore({ history, initialState: {} });
const render = () => {
ReactDOM.render(
<AppContainer>
<Provider store={store}>
<App history={history} />
</Provider>
</AppContainer>,
document.getElementById('root')
);
};
render(App);
if (module.hot) {
module.hot.accept('./Main/App', () => {
return render();
});
module.hot.accept('./store/rootReducer', () => {
store.replaceReducer(createRootReducer(history));
});
}
I'm getting errors like:
- Error TypeScript '>' expected. 22:17
Which is complaining about:
<AppContainer>
and here:
- the left hand side of an arithmetic operation must be of type any
<App history={history} />
I don't have 'strict' set in my config.
Any advice on this. thanks
I'm using an ejected create-react-app codebase and trying to setup both redux-saga and redux-promise middlewares. However redux-saga seems disabled no matter how I approach it or will sometimes report an error indicating that applyMiddleware hasn't been run against the sagaMiddleware object. (Please excuse the slightly convoluted sample code.)
import React from 'react';
import ReactDOM from 'react-dom';
import * as serviceWorker from './serviceWorker';
import { Provider } from 'react-redux';
import { createStore, applyMiddleware } from 'redux';
import ReduxPromise from 'redux-promise';
import { Route, BrowserRouter, Switch } from 'react-router-dom';
import reducersP1 from './p1/reducers';
import reducersP2 from './p2/reducers';
import Wrapper from './global/containers/wrapper';
import AppWrap from "./p1/components/app_wrap";
import AppWrap2 from "./p2/containers/app_wrap";
import createSagaMiddleware from 'redux-saga';
import rootSaga from './p2/sagas';
const appConfig = {
p1: {
reducers: reducersP1,
component: AppWrap,
namespace: "p1",
api: '/api'
},
p2: {
reducers: reducersP2,
component: AppWrap2,
namespace: "p2",
api: '/api2'
}
};
const { api, reducers, component, namespace } = process.env.REACT_APP_PORTFOLIO==="1"? appConfig.p1: appConfig.p2;
const WrapComponent = component;
export const apiPath = api;
const sagaMiddleware = createSagaMiddleware();
// Approach A.
const createStoreWithMiddleware = applyMiddleware(sagaMiddleware, ReduxPromise)(createStore);
const store = createStoreWithMiddleware(reducers);
// Approach B.
// const storeWithMiddleware = createStore(
// reducers,
// applyMiddleware(sagaMiddleware, ReduxPromise)
// );
// const store = storeWithMiddleware;
sagaMiddleware.run(rootSaga);
ReactDOM.render(
<Provider store={ store }>
<BrowserRouter>
<Switch>
<Route path="*" render={ (history) => <Wrapper namespace={ namespace }><WrapComponent history={ history } /></Wrapper> } />
</Switch>
</BrowserRouter>
</Provider>
,
document.getElementById('root')
);
// If you want your app to work offline and load faster, you can change
// unregister() to register() below. Note this comes with some pitfalls.
serviceWorker.unregister();
It seems redux-saga wasn't properly installed. Fixed with:
yarn add redux-saga
fist thanks for your attention.
I am trying to use fela for dynamically styling my component. Also for manage the states of app, we need to use redux. In fela we need to use a Provider to wrap all component of app. also in redux we have the same thing. for example the root of the app we have :
import { createRenderer } from 'fela'
import { Provider } from 'react-fela'
import { render } from 'react-dom'
import React from 'react'
const renderer = createRenderer()
render(
<Provider renderer={renderer}>
<App />
</Provider>,
document.getElementById('app')
)
and in root of redux app we have:
import React from 'react'
import { render } from 'react-dom'
import { Provider } from 'react-redux'
import { createStore } from 'redux'
import todoApp from './reducers'
import App from './components/App'
let store = createStore(todoApp)
render(
<Provider store={store}>
<App />
</Provider>,
document.getElementById('root')
)
And my problem is how to use this packages together.
when use as syntax in import can solve this problem. for example:
import React from 'react'
import { render } from 'react-dom'
import { Provider as ReduxProvider } from 'react-redux'
import { Provider as FelaProvider } from 'react-fela'
import { createStore } from 'redux'
import todoApp from './reducers'
import App from './components/App'
let store = createStore(todoApp)
render(
<ReduxProvider store={store}>
<FelaProvider>
<App />
</FelaProvider>
</ReduxProvider>,
document.getElementById('root')
)