Connect redux reducers with reduxform in nextjs - reactjs

I'm trying to combine reduxForm with my custom reducers inside nextjs with no luck. I'm using this as initial working example: https://github.com/zeit/next.js/blob/master/examples/with-redux-wrapper/store.js
When I add reduxForm according to their docs, I get error: Cannot read property 'source' of undefined , which means that store doesn't even exist.
How my store.js looks now:
import { createStore, applyMiddleware, combineReducers, compose } from 'redux';
import { reducer as formReducer } from 'redux-form';
import thunkMiddleware from 'redux-thunk';
import axios from 'axios';
import getRootUrl from '../lib/api/getRootUrl';
const initialState = {
user: 0,
agent: 0,
};
export const actionTypes = {
FETCH_USER: 'FETCH_USER',
USER_AGENT: 'USER_AGENT',
};
// REDUCERS
const authReducer = (state = { user: 0 }, action) => {
switch (action.type) {
case 'FETCH_USER': return { user: action.payload };
default: return state;
}
};
const agentReducer = (state = { agent: 0 }, action) => {
switch (action.type) {
case 'USER_AGENT': return { agent: action.payload };
default: return state;
}
};
// ACTIONS
export const fetchUser = () => async (dispatch) => {
const ROOT_URL = getRootUrl();
const resUser = await axios.get(`${ROOT_URL}/api/current_user`);
dispatch({ type: actionTypes.FETCH_USER, payload: resUser.data });
};
export const getUserAgent = () => async (dispatch) => {
const ROOT_URL = getRootUrl();
const resAgent = await axios.get(`${ROOT_URL}/api/useragent`);
dispatch({ type: actionTypes.USER_AGENT, payload: resAgent.data });
};
const rootReducer = combineReducers({
authReducer,
agentReducer,
formReducer,
});
export const initStore = (newState = initialState) => createStore(
rootReducer,
newState,
compose(applyMiddleware(thunkMiddleware)),
);
Last working example. I tried to combine with-redux-wrapper syntax with reduxForm docs. reduxForm action and reducer don't work here: https://github.com/neone35/rearn/blob/master/server/store.js
How can I combine these two to use reduxForm inside my component containing Field components?

I solved my problem after reading more of this documentation:
https://redux.js.org/recipes/structuring-reducers/initializing-state
https://redux.js.org/recipes/structuring-reducers/using-combinereducers
Also, I've logged out console to see what props my component is receiving to resolve problem faster.
How my store.js looks now:
import { createStore, applyMiddleware, combineReducers, compose } from 'redux';
import { reducer as formReducer } from 'redux-form';
import thunkMiddleware from 'redux-thunk';
import axios from 'axios';
import getRootUrl from '../lib/api/getRootUrl';
const initialState = {
user: '0',
agent: '0',
};
export const actionTypes = {
FETCH_USER: 'FETCH_USER',
USER_AGENT: 'USER_AGENT',
};
// REDUCERS
const authReducer = (state = null, action) => {
switch (action.type) {
case actionTypes.FETCH_USER:
return action.payload || false;
default:
return state;
}
};
const agentReducer = (state = null, action) => {
switch (action.type) {
case actionTypes.USER_AGENT:
return action.payload || false;
default:
return state;
}
};
export const rootReducer = combineReducers({
user: authReducer,
agent: agentReducer,
form: formReducer,
});
// ACTIONS
export const fetchUser = () => async (dispatch) => {
const ROOT_URL = getRootUrl();
const resUser = await axios.get(`${ROOT_URL}/api/current_user`);
dispatch({ type: actionTypes.FETCH_USER, payload: resUser.data });
};
export const getUserAgent = () => async (dispatch) => {
const ROOT_URL = getRootUrl();
const resAgent = await axios.get(`${ROOT_URL}/api/useragent`);
dispatch({ type: actionTypes.USER_AGENT, payload: resAgent.data });
};
export const initStore = (newInitialState = initialState) =>
createStore(rootReducer, newInitialState, compose(applyMiddleware(thunkMiddleware)));
I separated all my reducers into functions which initialize state to null (because initStore gets passed zeros, and preloadedState (initialState) always has priority). I return raw value from action creator (without object) and it gets passed straight to combineReducers (which creates object) which is rootReducer inside createStore.

Related

Error: Actions must be plain objects. Use custom middleware for async actions. How to solve it?

I am having this error in react-redux. I don't know how to solve it. I wanted to send a param which is taken from an api to another api and fetch results.
This is my store
import { createStore, applyMiddleware, compose } from "redux";
import thunk from "redux-thunk";
import { composeWithDevTools } from "redux-devtools-extension";
import rootReducer from "./Reducers/rootReducer";
const store = createStore(
rootReducer,
{},
compose(applyMiddleware(thunk), composeWithDevTools())
);
export default store;
This is my reducer code:
import * as types from "../Actions/types";
const initialState = {
posts: [],
table: [],
};
const postReducer = (state = initialState, action) => {
switch (action.type) {
case types.FETCH_DATA:
return {
...state,
posts: action.payload,
};
case types.FETCH_TABLE:
return {
...state,
table: action.payload,
};
default:
return {
...state,
};
}
};
export default postReducer;
This is my action code
export const getData = (from_userpart) => async (dispatch) => {
try {
const { data } = await api.getData(from_userpart);
dispatch({
type: types.FETCH_TABLE,
payload: data,
});
} catch (error) {
console.log(error);
}
};
When i use this code on the parent component,it works but i want to use that in my child component but it gives this error as i mentioned on the label. How to solve that?
useEffect(() => {
dispatch(getData(phone));
}, [dispatch]);
Change the setting of your store, your enhancer should come second.
let composeEnhancers = null
if (process.env.NODE_ENV === 'development') {
composeEnhancers = window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__ || compose
} else {
composeEnhancers = compose
}
const store = createStore(
rootReducer,
composeEnhancers(applyMiddleware(thunk))
);
I've noticed that i had imported my function from api folder not the action one. It works now

Reactjs Redux applymiddleware not calling?

I'm using Redux for the first time and I'm getting this error on the redux devtools:
error: "Actions must be plain objects. Use custom middleware for async actions."
This is my store.js code:
import {createStore, combineReducers, applyMiddleware, compose} from 'redux';
import thunk from 'redux-thunk';
import {productListReducer} from './reducers/productReducers';
const initialState = {};
const reducer = combineReducers ( {
productList: productListReducer,
})
const composeEnhancer = window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__ || compose;
const store = createStore(reducer, initialState, composeEnhancer(applyMiddleware(thunk)));
export default store;
And the productReducers.js where I have the switch for PRODUCT_LIST_FAIL:
import { PRODUCT_LIST_REQUEST, PRODUCT_LIST_SUCCESS, PRODUCT_LIST_FAIL } from "../constants/productConstants";
function productListReducer(state = {products:[]}, action) {
switch (action.type) {
case PRODUCT_LIST_REQUEST:
return {loading: true};
case PRODUCT_LIST_SUCCESS:
return {loading: false, products: action.payload};
case PRODUCT_LIST_FAIL:
return {loading: false, error: action.payload};
default:
return state;
}
}
export {productListReducer};
The import from the last code comes from here (productConstants.js):
export const PRODUCT_LIST_REQUEST = 'PRODUCT_LIST_REQUEST';
export const PRODUCT_LIST_SUCCESS = 'PRODUCT_LIST_SUCCESS';
export const PRODUCT_LIST_FAIL = 'PRODUCT_LIST_FAIL';
productActions.js:
import axios from 'axios';
import {
PRODUCT_LIST_REQUEST,
PRODUCT_LIST_SUCCESS,
PRODUCT_LIST_FAIL,
} from '../constants/productConstants';
const listProducts = () => async(dispatch) => {
try {
dispatch(PRODUCT_LIST_REQUEST);
const {data} = await axios.get("/api/products");
dispatch({type: PRODUCT_LIST_SUCCESS, payload: data});
}
catch(error) {
dispatch({type: PRODUCT_LIST_FAIL, payload: error.message});
}
}
export {listProducts}
guys.
My bad, I was writing dispatch(PRODUCT_LIST_REQUEST); instead of dispatch({type: PRODUCT_LIST_REQUEST}); inside the try-catch in productActions.js, that's why the PRODUCT_LIST_REQUEST was assumed as a string and not an object...

Why my action is not triggering in the reducer?

does anyone knows why nothing happens when the button is clicked?
im trying to fetch the movielist from the server when the button is clicked but it doesnt even shows that the action is working the way i expected to be.
my react index js
import React from 'react';
import { connect } from 'react-redux';
import { getMovies } from '../../actions/movieActions';
const Home = ({ movie }) => {
const handleClick = () => {
getMovies();
};
return (
<div>
<button onClick={() => handleClick()}>Get Movies</button>
</div>
);
};
const mapStateToProps = state => ({
movie: state.movie.movies
});
export default connect(mapStateToProps, { getMovies })(Home);
my rootReducer
import { combineReducers } from 'redux';
import movieReducer from './movieReducer';
export default combineReducers({
movie: movieReducer
// log is what we are calling our state
});
my movie reducer
import { GET_MOVIES } from '../actions/types';
const initialState = {
movies: null
};
export default (state = initialState, action) => {
switch (action.type) {
case GET_MOVIES:
return {
...state,
movies: action.payload
};
default:
return state;
}
};
my movie actions
import { GET_MOVIES } from './types';
// get movies from server
export const getMovies = () => async dispatch => {
try {
const res = await fetch('http://localhost:5000/api/movies');
const data = await res.json();
dispatch({
type: GET_MOVIES,
payload: data
});
} catch (error) {
console.log(error);
}
};
my types.js
export const GET_MOVIES = 'GET_MOVIES';
my 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 store = createStore(
rootReducer,
initialState,
composeWithDevTools(applyMiddleware(...middleware))
);
export default store;
Your error is in calling the action directly, instead of the connected getMovies prop here:
const handleClick = () => {
getMovies();
};
Should be:
const handleClick = () => {
this.props.getMovies();
};

Actions must be plain objects. Use custom middleware for async actions Saga thunk I do have so far in my store

The problem is:
I'm trying to use redux-saga in my react app, but i still has this error: Actions must be plain objects. Use custom middleware for async actions. Code it seems correct but no idea why gives that error. I'll be glad for all the help. I'm fighting with it for about two days and still doesn't have a solution. I tried to look up, but I still have this error.
action...
import { GET_DISTRICTS} from '../../constants';
const getAdres = async (url) => {
let response = await fetch(url);
let data = await response.json();
let list = [];
data.AdresList.Adresler.Adres.forEach((item) => {
console.info(item);
list.push({
label: item.ADI,
value: item.ID
});
});
return list;
};
export const actions = {
handleGetDistrictsData: async () => {
let districts = await getAdres(`url is here`);
return {
type: GET_DISTRICTS,
payload: districts
};
},
reducer...
import { GET_DISTRICTS } from '../../constants';
export const initialState = {
districts: [],
quarters: [],
streets: [],
doors: [],
districtSelected: false,
districtSelectedID: null,
quarterSelected: false,
quarterSelectedID: null,
streetSelected: false,
streetSelectedID: null,
doorSelected: false,
doorSelectedID: null
};
export default (state = initialState, action) => {
switch (action.type) {
case GET_DISTRICTS:
return {
...state,
districts: action.payload
};
default:
return state;
}
};
component...
import React, { useEffect, useState } from 'react';
import { connect } from 'react-redux';
import { bindActionCreators } from 'redux';
import { actions as addressActions } from '../../../../redux/actions/address';
import Select from 'react-select';
const Districts = (props) => {
let [ fetchedData, setFetchedData ] = useState(false);
useEffect(() => {
props.handleGetDistrictsData();
setFetchedData(true);
});
return (
<React.Fragment>
<Select
name='adresSelect'
options={props.address.districts}
onChange={props.handleDistrictChange}
placeholder='Please Select'
/>
</React.Fragment>
);
};
const mapStateToProps = (state) => ({
address: state.address
});
const mapDispatchToProps = function(dispatch) {
return bindActionCreators({ ...addressActions }, dispatch);
};
export default connect(mapStateToProps, mapDispatchToProps)(Districts);
-------------
import React from 'react';
import Districts from './Districts';
const AddressSearchWidget = (props) => {
return (
<React.Fragment>
<Districts />
</React.Fragment>
);
};
export default AddressSearchWidget
store...
import { applyMiddleware, combineReducers, compose, createStore } from 'redux';
import createSagaMiddleware from 'redux-saga';
import rootSaga from './sagas/index';
import * as reducers from './';
export function initStore() {
const composeEnhancer = window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__ || compose;
const rootReducer = combineReducers(reducers);
const sagaMiddleware = createSagaMiddleware();
const store = createStore(rootReducer, composeEnhancer(applyMiddleware(sagaMiddleware)));
// Run sagas
sagaMiddleware.run(rootSaga);
return store;
}
handleGetDistrictsData returns a promise (all async functions return promises). You cannot dispatch a promise in plain redux saga, and redux-saga does not change this. Instead, dispatch a normal action, and have that action run a saga. The saga can then do async things, and when it's done dispatch another action. The reducer listens only for that second action.
// Actions:
export const getDistrictsData = () => ({
type: GET_DISTRICTS,
})
export const districtsDataSuccess = (districts) => ({
type: DISTRICTS_DATA_SUCCESS,
payload: districts
})
// Sagas:
export function* watchGetDistricts () {
takeEvery(GET_DISTRICTS, getDistricts);
}
function* getDistricts() {
let response = yield fetch(url);
let data = yield response.json();
let list = [];
data.AdresList.Adresler.Adres.forEach((item) => {
console.info(item);
list.push({
label: item.ADI,
value: item.ID
});
});
yield put(districtsDataSuccess(list));
}
// reducer:
export default (state = initialState, action) => {
switch (action.type) {
case DISTRICTS_DATA_SUCCESS:
return {
...state,
districts: action.payload
};
default:
return state;
}
};

Redux action type is PROBE_UNKNOWN_ACTION

I'm calling the action requestLoadOrders to fetch the orders I need. I'm dispatching with type: REQUEST and afterwards with SUCCESS or FAILURE. The fetch succeeded because my orders are in the payload in the redux dev-tools, but the action that I receive in my reducer is ##redux/PROBE_UNKNOWN_ACTION_z.r.p.l.z. I found a thread about this here, however I can't seem to find what I'm doing wrong?
actions.js
import {
LOAD_ORDERS_REQUEST,
LOAD_ORDERS_SUCCESS,
LOAD_ORDERS_FAILURE
} from './constants';
import { fetchOrders } from '../../api';
export const requestLoadOrders = () => {
return (dispatch, getState) => {
dispatch({ type: LOAD_ORDERS_REQUEST });
fetchOrders().then(orders => {
dispatch({ type: LOAD_ORDERS_SUCCESS, payload: orders });
}).catch(error => {
console.error(error);
dispatch({ type: LOAD_ORDERS_FAILURE, payload: error });
});
};
};
reducer.js
import {
LOAD_ORDERS_REQUEST,
LOAD_ORDERS_SUCCESS,
LOAD_ORDERS_FAILURE
} from './constants';
const initialState = {
orders: []
};
const orderReducer = ( state = initialState, { payload, type }) => {
switch (type) {
case LOAD_ORDERS_REQUEST :
return state;
case LOAD_ORDERS_SUCCESS :
return { ...state, orders: payload};
case LOAD_ORDERS_FAILURE :
return { ...state, error: payload.error};
default :
return state;
}
};
export default orderReducer;
My actions get dispatched correctly, but I suppose there's a problem with the reducer receiving its data. Therefor I also added my store and combined reducers files.
store.js
import { createStore, applyMiddleware, compose } from 'redux';
import { routerMiddleware } from 'react-router-redux';
import createHistory from 'history/createBrowserHistory';
import thunk from 'redux-thunk';
import makeRootReducer from './reducers';
export const history = createHistory();
const initialState = {}
const enhancers = [];
const middleware = [ routerMiddleware(history), thunk ];
if (process.env.NODE_ENV === 'development') {
const devToolsExtension = window.devToolsExtension;
if (typeof devToolsExtension === 'function') {
enhancers.push(devToolsExtension());
}
}
const composedEnhancers = compose(
applyMiddleware(...middleware),
...enhancers
);
const store = createStore(
makeRootReducer,
initialState,
composedEnhancers
);
export default store;
reducers.js
import { combineReducers } from 'redux';
import orderReducer from '../modules/Order/reducer';
export const makeRootReducer = asyncReducers => {
return combineReducers({
order: orderReducer,
...asyncReducers
});
}
export default makeRootReducer;
I found my mistake. I should execute the makeRootReducer function by adding the brackets after the word in createStore().
Updated the createStore() part of store.js to:
const store = createStore(
makeRootReducer(),
initialState,
composedEnhancers
);
was the fix.

Resources