Redux saga called multiple times (2 or 3 times) Nextjs - reactjs

I am using Redux and Redux saga with Nextjs, i wrapped the store on _app.js file and when i call the api with post or get requests Redux-Saga is getting called at least two times, specially for post requests for example if i want to register a user using the api it is registering the user two times on the database
PS: I am using rootSaga and i am not calling a saga twice there
This is my store file:
import { createStore, applyMiddleware, compose } from "redux";
import createSagaMiddleware from "redux-saga";
import reducers from "./reducers";
import sagas from "./sagas";
const sagaMiddleware = createSagaMiddleware();
const middlewares = [sagaMiddleware];
export function configureStore(initialState) {
const store = createStore(
reducers,
initialState,
compose(applyMiddleware(...middlewares))
);
sagaMiddleware.run(sagas);
if (module.hot) {
module.hot.accept("./reducers", () => {
const nextRootReducer = require("./reducers");
store.replaceReducer(nextRootReducer);
});
}
return store;
}
export const wrapper = createWrapper(configureStore, { debug: true });
And this is my _app.js file
import "../styles/styles.global.scss";
import "../styles/Home.module.scss";
import React from "react";
import App, { Container } from "next/app";
import { Provider, connect } from "react-redux";
import withRedux from "next-redux-wrapper";
import { configureStore, wrapper } from "../libs/store";
const context = React.createContext(undefined);
class MyApp extends App {
static async getInitialProps({ Component, ctx }) {
if (Component.getInitialProps) {
pageProps = await Component.getInitialProps(ctx);
}
return { ...pageProps };
}
render() {
const { Component, pageProps, router } = this.props;
return (
<Provider store={configureStore()} context={context}>
<Component {...pageProps} key={router.asPath} />
</Provider>
);
}
}
export default wrapper.withRedux(MyApp);
Thank you.

I Fixed it by removing the provider from _app.js and deleting _document.js
PS: This solutions is for Nextjs >= 10.0.0

Related

Next redux wrapper arise error of Cannot read property 'getState' of undefined

In this project, I use the next js library along with redux and I also use next redux wrapper I do everything as mentioned in the next redux wrapper document but it is not working properly here it arises the getState of undefined error as I display on the above image. So here I debugged all the code but I could not found the point from where this error is arising.
I listed the code below which I have done in my react js files.
_app.js
import React from "react";
import App from "next/app";
import { Provider } from "react-redux";
import withRedux from "next-redux-wrapper";
import makeStore from ".././src/config/store";
class ConsoleApp extends App {
/**
* Render View
*/
render() {
const { Component, pageProps, store } = this.props;
return (
<Provider store={store}>
<Component {...pageProps} />
</Provider>
);
}
}
export default withRedux(makeStore)(ConsoleApp);
root-reducer.js
/**
* Import Dependencies
*/
import { combineReducers } from "redux-immutable";
/**
* Import Reducers
* All Reducers used in the App must be declared here!
*/
import GraphicReducer from "../modules/Graphics/reducer";
import UserReducer from "../modules/User/reducer";
/**
* Combine the Reducers
*/
const reducers = combineReducers({
graphics: GraphicReducer,
user: UserReducer
});
/**
* Export the combined Reducers
*/
export default reducers;
store.js
import { createStore, applyMiddleware } from "redux";
import { composeWithDevTools } from "redux-devtools-extension";
import reducers from "./root-reducer";
import thunk from "redux-thunk";
/**
* Prepare the Redux Store
*/
const composedMiddlewares = applyMiddleware(thunk);
const storeEnhancers = composeWithDevTools({
name: "React-node-test"
})(composedMiddlewares);
const makeStore = () => {
createStore(reducers, storeEnhancers);
};
export default makeStore;
Please use the below snippet as you forgot to return the createStore(reducers, storeEnhancers);
const makeStore = () => {
return createStore(reducers, storeEnhancers);
};

How to fix Uncaught TypeError: Cannot read property 'getState' of undefined?

I am trying to use React with Redux for the frontend part with django rest framework in the backend. Got the issue getState in Provider tag in App component because of issue in store. And when i try to use the map function in the Words.js, I get error of undefined use of map. And I believe this is because of value of the array is null. Hence to fixed this error of getState.
Got this error even on including the store in Provider of App component when a reducers was not defined.
When I load a static array it does get rendered properly in the specific component.
This is Redux Store in the filename:store.js
import { createStore, applyMiddleware } from "redux";
import { composeWithDevTools } from 'redux-devtools-extension';
import thunk from 'redux-thunk';
import rootReducer from './reducers'
const initialState = {};
const middleware = [thunk];
const enhancer = composeWithDevTools(applyMiddleware(...middleware));
const store = createStore(
rootReducer,
initialState,
enhancer
);
export default store;
The index.js file is below
import App from './components/App'
import ReactDOM from 'react-dom';
import React from 'react';
ReactDOM.render(<App />, document.getElementById("app"));
They action types file types.js using django rest_framework to create the data.
export const GET_WORDS = "GET_WORDS";
The action file words.js
import { GET_WORDS } from "./types";
import axios from 'axios';
export const getWords = () => dispatch => {
axios.get('/api/words/')
.then(res => {
dispatch({
type: GET_WORDS,
payload: res.data
});
}).catch(err => console.log(err));
}
combined reducer file
import { combineReducers } from "redux";
import words from './words';
export default combineReducers({
words
});
The reducer file word.js
import { GET_WORDS } from '../actions/types';[enter image description here][1]
const initialState = {
words: []
}
export default function (state = initialState, action) {
switch (action.type) {
case GET_WORDS:
return {
...state,
words: action.payload
}
default:
return state;
}
}
The Component in which the words list will be called: Words.js
import React, { Component, Fragment } from 'react';
import PropTypes from 'prop-types';
import { connect } from 'react-redux';
import { getWords } from "../../../actions/words";
export class Words extends Component {
static propTypes = {
words: PropTypes.array.isRequired,
getWords: PropTypes.func.isRequired
};
componentDidMount() {
this.props.getWords();
}
render() {
return (
<Fragment>
Hi
</Fragment>
)
}
}
const mapStateToProps = state => ({
words: state.words.words
});
export default connect(mapStateToProps, { getWords })(Words);
And finally the App component
import React, { Component, Fragment } from 'react';
import Footer from './Layout/Footer/Footer';
import Header from './Layout/Header/Header';
import WordsDashboard from './Content/Words/WordsDashboard';
import { store } from '../store';
import { Provider } from "react-redux";
import { Words } from './Content/Words/Words';
export class App extends Component {
render() {
return (
<Provider store={store}>
<Fragment>
<Header />
React Buddy
<Words />
<Footer />
</Fragment>
</Provider>
)
}
}
export default App;
Your initialState has only words prop, so when mapping it to props you have one extra words level. Try changing it to:
const mapStateToProps = state => ({
words: state.words
});
Also you need to use mapDispatchToProps for getWords, since in your current code you're missing dispatch wrapper:
const mapDispatchToProps = dispatch => ({
getWords: () => dispatch(getWords())
})
export default connect(mapStateToProps, mapDispatchToProps)(Words);

How to apply async react redux middleware

I'm a beginner in react.
I'd like to use the react redux to request api.
Error: Actions must be plain objects. Use custom middleware for async actions. An error has occurred.
Please help me with any problems.
I'd like to ask you how redux middleware should be applied.
action/index.js
export const fetchActionMovies = async () => {
const request = await axios.get(`${BASE_URL}/discover/movie?api_key=${API_KEY}&with_genres=28`)
return {
type: FETCH_ACTION_MOVIES,
payload: request
}
}
reducers/reducerActionMovies.js
import { FETCH_ACTION_MOVIES } from '../actions/index';
export default function (state = {}, action) {
switch (action.type) {
case FETCH_ACTION_MOVIES:
const data = action.payload.data.results;
return { ...state, data }
default:
return state;
}
}
container/ActionMovie.jsx
import React from 'react';
import { useSelector, useDispatch } from 'react-redux';
import { fetchActionMovies } from '../store/actions/index';
const ActionMovies = () => {
const dispatch = useDispatch();
const fetch = dispatch(fetchActionMovies());
console.log(fetch);
return (
<div>
<h1>Action Movies</h1>
</div>
)
}
export default ActionMovies;
index.js
import React from 'react';
import ReactDOM from 'react-dom';
import App from './App';
import * as serviceWorker from './serviceWorker';
import { Provider } from 'react-redux';
import { createStore, applyMiddleware } from 'redux';
import { composeWithDevTools } from 'redux-devtools-extension';
import ReduxThunk from 'redux-thunk';
import rootReducer from './store/reducers';
const store = createStore(
rootReducer,
composeWithDevTools(applyMiddleware(ReduxThunk))
);
ReactDOM.render(
<Provider store={store}><App /></Provider>,
document.getElementById('root')
);
serviceWorker.unregister();
Error: Actions must be plain objects. Use custom middleware for async actions.
First, even if you're going to use redux-thunk, you need to break up your action into three parts to track the asynchronous state of the request.
const FETCH_ACTION_MOVIES_REQUEST = "FETCH_ACTION_MOVIES_REQUEST";
const FETCH_ACTION_MOVIES_SUCCESS = "FETCH_ACTION_MOVIES_SUCCESS";
const FETCH_ACTION_MOVIES_FAILURE = "FETCH_ACTION_MOVIES_FAILURE";
You should create three actions that use these types that your reducer will track. Now, if you're not going to use redux-thunk... you will need to perform this fetch in your component. However, if you are using redux-thunk you can create an action like this:
export const fetchActionMovies = () => dispatch => {
dispatch(fetchActionMoviesRequest());
return axios.get(`${BASE_URL}/discover/movie?api_key=${API_KEY}&with_genres=28`).then(({
data
}) => {
dispatch(fetchActionMoviesSuccess(data));
}).catch(error => {
dispatch(fetchActionMoviesFailure(error));
})
}
Another option to consider is redux-saga.

How to write unit test redux connected components in next.js app with Jest end Enzyme

In React Single Page App, we need to separate the logic of createStore to another component (usually called <Root />) to reuse it in your test file to let connect function link with the store
Root.js
import React from "react";
import { Provider } from "react-redux";
import { createStore } from "redux";
import reducers from "reducers";
import { applyMiddleware } from "redux";
import reduxPromise from "redux-promise";
const appliedMiddlewares = applyMiddleware(reduxPromise);
export default ({ children, initialState = {} }) => {
const store = createStore(reducers, initialState, appliedMiddlewares);
return <Provider store={store}>{children}</Provider>;
};
And then in your test file, to mount or shallow your component, your code should look like this:
import Root from "Root";
let mounted;
beforeEach(() => {
mounted = mount(
<Root>
<CommentBox />
</Root>
);
});
But for the case of Next.JS, the logic to let redux works with it was implemented in _app.js file, with some wrapper components (<Container>, <Component> ) that I do not know how it works so I could not find the way to separate the createStore logic
_app.js
import App, { Container } from "next/app";
import React from "react";
import Root from '../Root';
import withReduxStore from "../lib/with-redux-store";
import { Provider } from "react-redux";
class MyApp extends App {
render() {
const { Component, pageProps, reduxStore } = this.props;
return (
<Container>
<Provider store={reduxStore}>
<Component {...pageProps} />
</Provider>
</Container>
);
}
}
export default withReduxStore(MyApp);
Anyone knows it ? Many many thanks for helping me solve this.
Possibly I'm late adding a response, but this is what I did and worked!
First I imported the custom app:
import App from "../_app";
import configureStore from "redux-mock-store";
import thunk from "redux-thunk";
import { state } from "../../__mocks__/data";
const middlewares = [thunk];
const mockStore = configureStore(middlewares)(state);
Then I mocked the getInitialProps from the _app.js like:
const context = {
store: mockStore,
req: {
headers: {
cookie: ""
}
}
};
const props = await App.getInitialProps({
ctx: context,
Component: {
getInitialProps: jest.fn(() => {
return Promise.resolve({ ... });
})
}
});
Then, debugging over node_modules\next-redux-wrapper\src\index.tsx I noticed how the initialState must be set.
Then I added the following code:
delete window.__NEXT_REDUX_STORE__;
const wrapper = shallow(<App {...props} initialState={state}/>);
expect(toJson(wrapper)).toMatchSnapshot();
And run the tests, everything now works as expected.
If there is a cleaner solution please let me know.
I hope It works for you!

NextJS and Redux-thunk: Getting 'Error: Circular structure in "getInitialProps"'

I'm trying to implement a simple test case for using Redux-thunks with Next JS but keep getting the error
Error: Circular structure in "getInitialProps" result of page "/".
https://err.sh/zeit/next.js/circular-structure
I have gotten this all to work once before, and am sure I'm making some obvious error.
I'd appreciate any help you could provide. I've been poking at this for an hour and I'm not seeing where I'm going wrong...
I've traced it down to the dispatch within my thunk, that is dispatch(getItemsSuccess(data)) in the following code in action-creators.js. That is, if I remove that dispatch, I don't get the error.
// action-creators.js
import {GET_ITEMS_SUCCESS} from "./action-types"
import axios from 'axios'
export const getItemsSuccess = (data) => ({ type: GET_ITEMS_SUCCESS, data });
export const getItems = () => async (dispatch,getState) => {
try {
const data = await axios.get(`https://api.themoviedb.org/3/genre/movie/list?api_key=12345xyz`)
return dispatch(getItemsSuccess(data))
} catch(e) {
console.log(`error in dispatch in action-creators: ${e}`)
}
}
My _app.js is
import React from 'react'
import {Provider} from 'react-redux'
import App, {Container} from 'next/app'
import withRedux from 'next-redux-wrapper'
import configureStore from '../redux/configure-store'
class MyApp extends App {
static async getInitialProps({Component, ctx}) {
let pageProps = {}
if (Component.getInitialProps) {
pageProps = await Component.getInitialProps(ctx)
}
return {pageProps}
}
render() {
const {Component, pageProps, store} = this.props
return (
<Container>
<Provider store={store}>
<Component {...pageProps} />
</Provider>
</Container>
)
}
}
export default withRedux(configureStore, { debug: true })(MyApp);
and my index.js is
import React, {Component} from 'react'
import {connect} from 'react-redux'
import {getItems} from "../redux/action-creators"
class Index extends Component {
static async getInitialProps({store}) {
try {
await store.dispatch(getItems())
} catch(e) {
console.log(`error in dispatch in index.js: ${e.message}`)
}
}
render() {
return <div>Sample App</div>
}
}
export default connect(state => state)(Index)
and finally I configure the store thus
import { createStore, applyMiddleware } from 'redux';
import thunk from 'redux-thunk';
import rootReducer from './root-reducer';
const bindMiddleware = middleware => {
if (process.env.NODE_ENV !== 'production') {
const { composeWithDevTools } = require('redux-devtools-extension');
return composeWithDevTools(applyMiddleware(...middleware));
}
return applyMiddleware(...middleware);
};
function configureStore(initialState = {}) {
const store = createStore(
rootReducer,
initialState,
bindMiddleware([thunk]),
);
return store;
}
export default configureStore;
Again, any help much appreciated -- I have been going over this for some time and am not seeing the missing piece...
When you return data from axios, one has to access the data within the data, to wit, instead of
const data = await
axios.get(`https://api.themoviedb.org/3/genre/movie/list?api_key=12345xyz`)
return dispatch(getItemsSuccess(data))
I should have written
axios.get(`https://api.themoviedb.org/3/genre/movie/list?api_key=12345xyz`)
return dispatch(getItemsSuccess(data.data))
Why This Error Occurred
getInitialProps is serialised to JSON using JSON.stringify and sent to the client side for hydrating the page.
However, the result returned from getInitialProps can't be serialised when it has a circular structure.
Possible Ways to Fix It
Circular structures are not supported, so the way to fix this error is removing the circular structure from the object that is returned from getInitialProps. In your case you just need to extract appropriate data like #Cerulean explained.

Resources