Enabling both redux-promise and redux-saga middlewares appears to cause problems - javascript

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

Related

RTL "Received value must be an HTMLElement or an SVGElement. Received has value: null" due to having to use ReactDOM.render() in test

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();
});

How to fix error: Could not find router reducer in state tree, it must be mounted under "router"

I got - Error: Could not find router reducer in state tree, it must be mounted under "router".
I've read all the similar topics. Tryed different variants, but can't find solution. I still don't have enough knowledge to understand the subject. Plz help to fix this error if you know how.
index.js
import { render } from 'react-dom';
import { Provider, ReactReduxContext } from 'react-redux';
import React from 'react';
import { store, history} from './store';
import { Route, Switch } from 'react-router-dom';
import { ConnectedRouter } from 'connected-react-router'
import App from './components/App/App';
render(
<Provider store={store}>
<ConnectedRouter history={history} context={ReactReduxContext}>
<Switch>
<Route path="/" component={App} />
</Switch>
</ConnectedRouter>
</Provider>,
document.getElementById('root')
);
part of reducers.js
import { combineReducers } from 'redux';
import { routerReducer } from 'react-router-redux';
export default combineReducers({
router: routerReducer
});
store.js
import { applyMiddleware, createStore } from 'redux';
import { createLogger } from 'redux-logger';
import { promiseMiddleware, localStorageMiddleware } from './middleware';
import reducer from './reducers/reducers';
import { routerMiddleware } from 'react-router-redux'
import { createBrowserHistory } from 'history';
export const history = createBrowserHistory();
const getMiddleware = () => applyMiddleware(
routerMiddleware(history), promiseMiddleware, localStorageMiddleware, createLogger()
);
export const store = createStore(
reducer,
getMiddleware(),
);
tryed do like this:
in store.js
import { applyMiddleware, createStore } from 'redux';
import { createLogger } from 'redux-logger';
import { promiseMiddleware, localStorageMiddleware } from './middleware';
import reducer from './reducers/reducers';
import { createBrowserHistory } from 'history';
import { routerMiddleware } from 'connected-react-router';
export const history = createBrowserHistory();
const myRouterMiddleware = routerMiddleware(history);
const getMiddleware = () => applyMiddleware(
myRouterMiddleware, promiseMiddleware, localStorageMiddleware, createLogger()
);
export const store = createStore(
reducer(history),
getMiddleware()
);
in reducers.js
import authorization from './authorization';
import mainstate from './mainstate';
import home from './home';
import { combineReducers } from 'redux';
import { connectRouter } from 'connected-react-router'
export default (history) => combineReducers({
authorization,
mainstate,
home,
router: connectRouter(history)
});
got the same error :(
There are a couple of issues.
createStore(reducer, getMiddleware()) - the enhancer (middleware) needs to be the third argument, the second argument should be the store's initial state. See the docs here.
Try this instead
createStore(reducer, {}, getMiddleware())
The other issue is the version of the history package, with react-router-dom v5 you need to use history v4 (the latest version of which is 4.10.1) - history v5 is only compatible with react-router-dom v6.
In the Codesandbox you posted in your comment below, changing the following in package.json makes it work
"dependencies": {
...
"history": "^5.0.0", -> "history": "4.10.1",
...
}
Lot of solutions out there are pointing at history library version.
The version 5.x of the history package is causing the issues as above.
Could you please try downgrading the history version to 4.10.1 as suggested here

Error get 'Uncaught Error: Actions must be plain objects. Use custom middleware for async actions.' by redux-thunk

I try to write bellow code. but redux-thunk doesn't work.
Do you know how to resolve it?
When I exec this code, it can get this error.
createStore.js:113 Uncaught Error: Actions must be plain objects. Use custom middleware for async actions.
But I already installed redux-thunk. why does this error happen?
index.js
import { createDevTools } from 'redux-devtools';
import LogMonitor from 'redux-devtools-log-monitor';
import DockMonitor from 'redux-devtools-dock-monitor';
import React from 'react';
import ReactDOM from 'react-dom';
import { createStore, combineReducers, applyMiddleware, compose } from 'redux';
import { Provider } from 'react-redux';
import { Router, Route, IndexRoute, browserHistory } from 'react-router';
import { syncHistoryWithStore, routerReducer } from 'react-router-redux';
import thunk from 'redux-thunk';
import * as storage from './persistence/storage';
import randomToken from './utlis/random';
import * as reducers from './reducers';
import {
App,
Home
} from './components';
const reducer = combineReducers({
...reducers,
routing: routerReducer
});
if (!storage.get('token')) {
storage.put('token', randomToken());
}
const initialState = {
application: {
token: storage.get('token')
}
};
const DevTools = createDevTools(
<DockMonitor toggleVisibilityKey="ctrl-h" changePositionKey="ctrl-q">
<LogMonitor theme="tomorrow" preserveScrollTop={false} />
</DockMonitor>
);
const store = createStore(
reducer,
initialState,
compose(
applyMiddleware(
thunk
),
DevTools.instrument()
)
);
const history = syncHistoryWithStore(browserHistory, store);
ReactDOM.render(
<Provider store={store}>
<div>
<Router history={history}>
<Route path="/" component={App}>
<IndexRoute component={Home}/>
</Route>
</Router>
</div>
</Provider>,
document.getElementById('app')
);
Map.js
import React from 'react';
import { connect } from 'react-redux';
import * as actions from '../actions/cityForcast';
export class Home extends React.Component {
constructor (props, context) {
super(props, context);
}
componentWillMount () {
this.props.dispatch(actions.search(this.props.token));
}
render() {
return (
<div>
<button onClick={() => actions.search()}>Search</button>
</div>
);
}
}
export default connect(({ application, cityForecast }) => ({ application, cityForecast }))(Home);enter code here
cityForcast.js
export function search(token) {
return dispatch => {
console.log(token);
};
}
I could resolve that for myself. I needed to install redux-thunk as devDependencies as well.

Several errors when integrating react router to React Redux application

I'm trying to utilize the React router package in my React + Redux application, and doing so gives me the following errors:
Unexpected key "listings" found in previous state received by the reducer. Expected to find one of the known reducer keys instead: "routing". Unexpected keys will be ignored.
Uncaught TypeError: Cannot read property 'accounts' of undefined
ReactDOMComponentTree.js?1a2c:107Uncaught TypeError: Cannot read property '__reactInternalInstance$c5skqk6ty0f83o5hzvf0i19k9' of null
Here is my code:
initialState.js:
export default {
listings: {
status: '',
searchBy: '',
accounts: []
}
};
index.js (root reducer file):
import { combineReducers } from 'redux';
import { routerReducer } from 'react-router-redux';
const rootReducer = combineReducers({
routing: routerReducer
});
export default rootReducer;
routes.js:
import React from 'react';
import { Route, IndexRoute } from 'react-router';
import App from './components/App';
export default (
<Route path="/" component={App}>
<IndexRoute component={App}/>
</Route>
);
configureStore.js:
import {createStore, compose, applyMiddleware} from 'redux';
import rootReducer from '../reducers';
import reduxImmutableStateInvariant from 'redux-immutable-state-invariant';
import thunk from 'redux-thunk';
export default function configureStore(initialState) {
const store = createStore(rootReducer, initialState, compose(
// Add other middleware on this line...
applyMiddleware(reduxImmutableStateInvariant(), thunk),
window.devToolsExtension ? window.devToolsExtension() : f => f // add support for Redux dev tools
)
);
if (module.hot) {
// Enable Webpack hot module replacement for reducers
module.hot.accept('../reducers', () => {
const nextReducer = require('../reducers').default; // eslint-disable-line global-require
store.replaceReducer(nextReducer);
});
}
return store;
}
index.js (entry point for app):
import React from 'react';
import {render} from 'react-dom';
import { Provider } from 'react-redux';
import { Router, browserHistory } from 'react-router';
import routes from './routes';
import configureStore from './store/configureStore';
import initialState from './reducers/initialState';
import objectAssign from 'object-assign';
import mockTableData from './data/MockTableData';
import App from './components/App';
import { syncHistoryWithStore } from 'react-router-redux';
const listings = objectAssign({}, initialState.listings, {accounts: mockTableData});
const initial = objectAssign({}, initialState, {listings});
const store = configureStore(initial);
const history = syncHistoryWithStore(browserHistory, store);
render(
<Provider store={store}>
<Router history={history} routes={routes} />
</Provider>, document.getElementById('app')
);
Any ideas?

webpack 2 hot reload not rerender

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);
},
);
}

Categories