Failed fetch causes infinite component life-cycle loop - javascript

With a connected container, I have a reducer that is wrapped by a higher order reducer (show below) to catch and handle errors. When calling a fetch request during componentDidMount and it fails, the connected container will unmount itself componentWillUnmount. This causes an infinite loop in the container as it will mount again, fetch will fail, and container will unmount itself.
Any ideas why having the higher order reducer in the connect component is causing this?
Error handling higher order reducer:
export const errorHandler = (reducer: (state: any, action: { type: string }, initialState: any) => {}) => {
const errorState = fromJS({
error: {
hasError: false,
message: "",
},
});
const initialState = errorState.merge(reducer(undefined, { type: undefined }, undefined));
return (state = initialState, action) => {
switch (action.type) {
case ACTIONS.SET_ERROR:
return state.setIn(["error", "hasError"], true)
.setIn(["error", "message"], action.message);
case ACTIONS.CLEAR_ERROR:
return state.set("error", errorState.get("error"));
default:
return reducer(state, action, initialState);
}
};
};
Example Container:
class Page extends Component {
componentDidMount() {
this.props.fetch(....);
}
componentWillUnmount() {
this.props.clearData();
this.props.cancelRequests();
}
}
export default connect(
(state) => ({
error: state.data.get("error", ""),
}),
{
clearError,
clearData,
cancelRequests,
},
)(Page);
Example Reducer:
export fetch = () => ({
type: ACTIONS.FETCH
});
export default errorHandler((state, action) => {
switch(action.type) {
default:
return state;
}
}));
Epic:
export const fetch = (action$: any, store: any, api: API) => {
return action$.ofType(ACTIONS.FETCH)
.mergeMap((action: any) =>
fromPromise(api.fetch(action.data))
.pluck("Data")
.map(data) =>
fetchFulfilled(data),
)
.catch((response) => {
const toPromise = typeof response.json === "function" ? response.json() : new Promise((resolve) => resolve(response));
return fromPromise(toPromise)
.pluck("Message")
.map((Message: string) =>
setError(Message));
})
.takeUntil(action$.ofType(ACTIONS.CANCEL_REQUESTS)));
};

Based on our conversation in the comments:
Typically components unmount because their parent no longer renders them. What does the parent of look like? It is likely where you would look to find why your component is unmounting.
I'm not aware of any situation where a component can unmount itself (without hacks)

I think you just need catch the error instead of letting the exception be caught by the React mounting code.
try {
this.props.fetch(....);
}
catch (e) {
//Do whatever is appropriate to handle the fetch failure. Maybe you want...
this.setState({ error: {hasError: true, message: '' + e});
}
I think the setState() call above is not right for your intended reducer implementation, but that is a separate problem you can solve (or ask more questions about). The main part of your problem seemed to be stopping the unmount/remount behavior.

Related

A property of the state changed by useReducer() is never displaying changes even when those are dispatched

I have been working with useReducer() so that I can share some state among some components. There is a property in the requestReducer called isLoading which default/initial state is set to false. There are two functions dispatching actions to the reducer (initializeLoading: changing isLoading to true, and getRequestResponse: changing isLoading back to false). But when the function that the reducer uses to initialize loading -initializeLoading-, is called by CallingComponent that uses the pointer of those functions to call them; the change of state inside the useEffect (which has a dependency for the requestState.isLoading), is never displayed in the console or called neither in the useCurrentRequest hook nor the CallingComponent, when it is supposed to be called; because in that first calling the isLoading prop, from false, turns into true, through the SEND action.type. Moreover when calling the getRequestResponse function (which launches a RESPONSE action.type => isLoading = false) from the CallingComponent, it should switch from true, to false, thereby it should be displayed also by the useEffect that is supposed to capture those changes. But nothing of that happens, instead nothing is displayed.
Here is the useCurrentRequest and the requestReducer:
import {useReducer, useCallback, useEffect} from 'react';
export const initialState = {
isLoading: false,
data: null,
}
const requestReducer = (state = initialState, action) => {
switch (action.type) {
case 'SEND':
return {...state,
isLoading: true,
};
case 'RESPONSE':
return {...state,
isLoading: false,
data: action.responseData
};
default:
return state;
}
}
//This is the hook with the logic that sets state using the reducer
const useCurrentRequest = () => {
const [requestState, dispatchRequestActions] = useReducer(requestReducer, initialState);
//The loading change of state is not displayed in the console
useEffect(() => {
console.log(requestState.isLoading);
}, [requestState.isLoading]);
const initializeLoading = useCallback(() => {
dispatchRequestActions({type: 'SEND'});
console.log(requestState);
},[]);
const getRequestResponse = useCallback(() => {
//some call to an api happens here
//Then the response is given using the reducer
dispatchRequestActions({type: 'RESPONSE', responseData: {someData:true}});
}, []);
return {
initializeLoadingPointer: initializeLoading,
getRequestResponsePointer: getRequestResponse,
isLoading: weatherRequestState.isLoading,
data: weatherRequestState.data,
}
}
export default useCurrentRequest;
And here is the CallingComponent, which uses the function pointers provided by the useCurrentRequest hook:
import {useEffect} from "react";
import useCurrentRequest from "../hooks/useCurrentRequest";
const CallingComponent = props => {
const {initializeLoadingPointer, getRequestResponsePointer, isLoading} = useCurrentWeather();
const getData = () => {
initializeLoadingPointer();
getRequestResponsePointer();
};
useEffect(() => {
console.log(isLoading);
}, [isLoading]);
return (<button onClick={getData}>Get data</button>)
}
The problem basically is that nothing is displayed after the Get Data button is clicked, when there is supposed to display in the console, true and false in that respective order, because of the useEffect() depending on isLoading and those functions changing isLoading, but again nothing is displayed.
I will appreciate any help on this.
Thanks! :)
I finally solved the issue by using async-await, the problem was that since the requests to those functions that were modifying the reducer were happening synchronously, the state was only updated until both of them were finished, and the function that was calling them has already finished too. So what I did was to block some pieces of code using promises via de async-await mode. Basically, I only modified useCurrentRequest() hook by replacing those 2 functions by this:
const useCurrentRequest = () => {
const [requestState, dispatchRequestActions] = useReducer(requestReducer, initialState);
useEffect(() => {
console.log(requestState.isLoading);
}, [requestState.isLoading]);
const sendRequestAsync = useCallback(async (city) => {
const query = `weather?q=${city}`;
console.log(query);
dispatchRequestActions({ type: "SEND" });
try {
const result = await fakeRequest();
dispatchRequestActions({ type: "RESPONSE", responseData: result.data });
} catch (error) {
dispatchRequestActions({ type: "ERROR", errorMessage: error.message });
}
}, []);
const fakeRequest = () => {
return new Promise((resolve, reject) => {
setTimeout(() => {
resolve({ data: "data" });
}, 1000);
});
};
return {
getRequestResponsePointer: sendRequestAsync,
isLoading: weatherRequestState.isLoading,
data: weatherRequestState.data,
}
}
export default useCurrentRequest;
That way I was able to see the changes using the useEffect() hook.

Async/Await seems to have different behaviour in class methods and function expressions (React Hooks, Redux-Thunk)

I'm migrating a class-based react system to hooks, and I'm facing some challenges which I can't understand.
Take a look at the snippet below:
async onSearchforOptions(elementId) {
await this.props.onFetchOperatingSystems()
//(3) [{…}, {…}, {…}]
console.log(this.props.operatingSystems)
}
In this method, I am dispatching an action to update the redux state, and right after this I'm logging the result to make sure the information was fetched and updated in the redux state.
The problem is that in an application which uses functional components, the result doesn't seem the same. Instead of updating the redux state and recovering the info right after, it simply doesn't seem to update the state, even if I'm using "await" and the very same actions and reducers the class component is using:
const onSearchforOptions = async (elementId) => {
await props.onFetchOperatingSystems()
//[]
console.log(props.operatingSystems)
}
My connection for both components (the class component and the functional component):
const mapStateToProps = state => {
return {
operatingSystems: state.operatingSystemReducer.operatingSystems
}
}
const mapDispathToProps = dispatch => {
return {
onFetchOperatingSystems: () => dispatch(actions.fetchOperatingSystems())
}
}
export default connect(mapStateToProps, mapDispathToProps)(productsForm)
My actions:
export const fetchOperatingSystemsStart = () => {
return {
type: actionTypes.FETCH_OPERATING_SYSTEMS_START
}
}
export const fetchOperatingSystemsFail = (error) => {
return {
type: actionTypes.FETCH_OPERATING_SYSTEMS_FAIL,
error: error
}
}
export const fetchOperatingSystemsSuccess = (operatingSystems) => {
return {
type: actionTypes.FETCH_OPERATING_SYSTEMS_SUCCESS,
operatingSystems: operatingSystems
}
}
export const fetchOperatingSystems = () => {
return dispatch => {
dispatch(fetchOperatingSystemsStart())
return axios.get(url)
.then(response => {
const fetchedData = []
for (let key in response.data) {
fetchedData.push({
...response.data[key],
id: response.data[key].id
})
}
dispatch(fetchOperatingSystemsSuccess(fetchedData))
})
.catch(error => {
if (error.response !== undefined) dispatch(fetchOperatingSystemsFail(error.response.data))
else dispatch(fetchOperatingSystemsFail(error))
})
}
}
My Reducer:
const initialState = {
operatingSystems: [],
loading: false
}
const fetchOperatingSystemsStart = (state) => {
return updateObject(state, { loading: true })
}
const fetchOperatingSystemsSuccess = (state, action) => {
return updateObject(state, { operatingSystems: action.operatingSystems, loading: false })
}
const fetchOperatingSystemsFail = (state) => {
return updateObject(state, { loading: false })
}
const reducer = (state = initialState, action) => {
switch (action.type) {
case actionTypes.FETCH_OPERATING_SYSTEMS_START: return fetchOperatingSystemsStart(state)
case actionTypes.FETCH_OPERATING_SYSTEMS_SUCCESS: return fetchOperatingSystemsSuccess(state, action)
case actionTypes.FETCH_OPERATING_SYSTEMS_FAIL: return fetchOperatingSystemsFail(state)
default: return state
}
}
export default reducer
updateObject function:
export const updateObject = (oldObject, updatedProperties) => {
const element = {
// The values of the object oldObject are being spread, at the same time the values of
// updatedProperties are (I'm taking out the attributes of both objects with the spread operator).
// In this case, since the names of the attributes are the same,
// the attributes (which were spread) of the first object will have their values replaced
// by the values of the second object's attributes.
...oldObject,
...updatedProperties
}
return element
}
My Goal:
Accoding to the snippet below, my goal is to dynamically search for options and update it in my form, which is in the component state.
const onSearchforOptions = async (elementId) => {
let elementUpdated
switch (elementId) {
case 'operatingSystem': {
await props.onFetchOperatingSystems()
console.log(props.operatingSystems)
elementUpdated = {
'operatingSystem': updateObject(productsForm['operatingSystem'], {
selectValue: {
value: props.selectedElement.operatingSystem ? props.selectedElement.operatingSystem.id : undefined,
label: props.selectedElement.operatingSystem ? props.selectedElement.operatingSystem.name : undefined
},
elementConfig: updateObject(productsForm['operatingSystem'].elementConfig, {
options: props.operatingSystems
})
})
}
break
}
case 'productType': {
await props.onFetchProductTypes()
elementUpdated = {
'productType': updateObject(productsForm['productType'], {
selectValue: {
value: props.selectedElement.productType ? props.selectedElement.productType.id : undefined,
label: props.selectedElement.productType ? props.selectedElement.productType.name : undefined
},
elementConfig: updateObject(productsForm['productType'].elementConfig, {
options: props.productTypes
})
})
}
break
}
default: break
}
const productsFormUpdated = updateObject(productsForm, elementUpdated)
setProductsForm(productsFormUpdated)
}
The props object passed to the render function initially is not going to be mutated; rather the props passed to your component on its next render will be updated. This is more in keeping with the flux architecture. You fire-and-forget an action, the reducer runs, and then your component is re-rendered with new props.
Before, this same thing was happening, but the new props were being assigned to this.props again. Since there's no meaningful "this" anymore, you can't use this pattern. Besides, depending on this behavior is not idiomatically the React way of doing things.
Update:
I think this is like a great number of cases I've also encountered where the React team seemed to overcorrect for a lot of use cases of people handling derived state poorly (see You Probably Don't Need Derived State). I've seen plenty of cases, like yours, where the now-deprecated componentWillReceiveProps lifecycle method solved this problem for class-based components very nicely.
Thankfully, useEffect now gives you something like a replacement. Think about it this way: when props.operatingSystems changes, you want to perform the effect of changing the state of your form. It's an unfortunate double update issue, but you had that before. Here's how you could go about writing that:
const [productsForm, setProductsForm] = useState(...);
useEffect(() => {
// Handle the case where props.operatingSystems isn't initialized?
if (!props.operatingSystems || !props.selectedElement.operatingSystem)
return;
setProductsForm({
...productsForm,
operatingSystem: {
...productsForm.operatingSystem,
selectValue: {
value: props.selectedElement.operatingSystem.id,
label: props.selectedElement.operatingSystem.name
},
elementConfig: {
...productsForm.operatingSystem.elementConfig,
options: props.operatingSystems
}
}
});
}, [props.operatingSystems]);
The way this works is that your effect code is only kicked off whenever your props.operatingSystems value changes since the last render. You can do a similar sort of effect for product types.
Another option which is maybe less elegant is for your async function that kicked off the redux actions to also resolve to a value which you can then use in your state setting code:
const operatingSystems = await props.onFetchOperatingSystems();
// ...now set your state
i usually implements thunks in a functional component like:
`export default connect(mapStateToProps, {fetchOperatingSystems})(productsForm)`
can you try this and comment back.

Redux thunk: wait for async function to dispatch

I'm creating a React Native application and using redux and redux-thunk to implement my API requests. I would like to know how I can wait for my action to be dispatched and make sure that my state has been updated in an async thunk logic. If I understand correctly, await will wait for the end of the thunk but the action is not dispatched yet. Although, as you can see in my usage, I need the state to be modified to proceed the rest of the code accordingly.
actions/user.js
export const tryLogin = (
email: string,
password: string,
sessionToken: string = ''
): Function => async (dispatch: Function) => {
const logUser = () => ({ type: LOG_USER })
const logUserSuccess = (data: any, infos: any) => ({
type: LOG_USER_SUCCESS,
data,
infos,
})
const logUserError = (signinErrorMsg: string) => ({
type: LOG_USER_ERROR,
signinErrorMsg,
})
dispatch(logUser())
try {
{ /* Some API requests via axios */ }
dispatch(logUserSuccess(responseJson, infos))
return true
} catch (error) {
{ /* Error handling code */ }
dispatch(logUserError(error.response.data.error))
return false
}
reducers/user.js
case LOG_USER:
return {
...state,
isLoggingIn: true,
}
case LOG_USER_SUCCESS:
return {
...state,
isLoggingIn: false,
data: action.data,
infos: action.infos,
error: false,
signinErrorMsg: '',
}
case LOG_USER_ERROR:
return {
...state,
isLoggingIn: false,
error: true,
signinErrorMsg: action.signinErrorMsg,
}
RegisterScreen.js
if (await trySignup(
emailValue,
firstNameValue,
lastNameValue,
passwordValue,
birthdateValue,
genderValue
)
) {
if (userReducer.data) {
navigation.navigate('Secured')
}
In Redux,
When an Action is dispatched to the store, it will update the state of the UI automatically with new props.
Instead of watching the dispatched action, You can add a flag in the reducer signUpSuccess similar to isLoggingIn flag and listen to the changes in componentDidUpdate lifecycle method.
trySignup can be called separately (like on an event, formSubmit, button click, etc.)
RegisterScreen.js
class RegisterScreen extends React.Component{
...
componentDidUpdate(prevProps) {
if (prevProps.signUpSuccess !== this.props.signUpSuccess){
if (this.props.signUpSuccess) {
navigation.navigate('Secured')
}
}
}
...
}
const mapStateToProps = (state) => ({
signUpSuccess: state.userReducer.signUpSuccess,
});
export default connect(mapStateToProps)(RegisterScreen);
If I understand correctly, await will wait for the end of the thunk
but the action is not dispatched yet.
Render can be update if any changes happens in props, so extract props in your render method and update UX as per change in props.
I would suggest use React native debugger to check your actions
and current saved state.

How to get data back from reducer with a async action

Redux newbie here
So I have this code that dispatches an async action and it uses promise to handle the request
So here is mapDispatchToProps
const mapDispatchToProps = (dispatch) => {
return {
fetchUserList: () => {
console.log('---In fetchUserList---');
dispatch({
type: FETCH_USER_LIST,
payload: new Promise((resolve, reject) => {
let xhr = new XMLHttpRequest();
console.log('xhr: ', xhr);
xhr.open('GET', 'http://localhost:5000/data/fetch/users');
xhr.onload = () => {
console.log(' --- in onload function ---');
console.log(xhr.responseText);
resolve(JSON.parse(xhr.responseText));
}
xhr.onerror = () => {
console.log(' --- in onerror function ---');
reject(xhr.statusText);
}
xhr.send();
})
});
}
what it does is it fetches array of json objects from the server which get map to
mapStateToProps
const mapStateToProps = (state) => {
return {
userlist: state.list_reducer,
};
};
Now here is the component I used both of above:
class Profile extends Component {
constructor(props) {
super(props);
this.state = {
listRender: false
};
}
render() {
console.log('In render: ');
console.log(this.props.userlist);
return (
// basic list rendering template
);
}
componentDidMount() {
this.props.fetchUserList();
this.setState({
listRender: true
});
}
Now It can see from the above that I use fetchUserList() in componentDidMount() to fetch data from the server and data does get send to the reducer
Here is the list_reducer.js:
export default function list_reducer(state = {
operation: '',
userlist: []
}, action) {
switch (action.type) {
case "FETCH_USER_FULFILLED":
console.log("FETCH_USER_FULFILLED");
console.log(action.payload);
return {
operation: 'success',
userlist: action.payload
}
case "UPDATE_USER_DETAILS_FULFILLED":
return {
operation: 'success',
userlist: action.payload
}
case "DELETE_USER_FULFILLED":
return {
operation: 'success',
userlist: action.payload
}
case "REGISTER_USER_FULFILLED":
return {
operation: 'success',
//userlist:
}
default:
return { ...state
}
}
};
Now instead of receiving newly fetched userlist I get the default data that is passed to reducer in this.props.userlist (mapStateToProps)
So the question is how do I get the newly fetched list instead of the default state data that is given to the reducer.
When making async calls with Redux I use Thunks
By default, Redux action creators don’t support asynchronous actions
like fetching data, so here’s where we utilise Redux Thunk. Thunk
allows you to write action creators that return a function instead of
an action. The inner function can receive the store methods dispatch
and getState as parameters.
So, if you wanted to make an API call, our action would look like this.
Actions.js (or your equivalent)
import axios from "axios";
export const receiveUserLift = json = {
return {
type: "RECEIVE_USERS",
payload: json
}
}
export function fetchUserList() {
return dispatch => {
return axios
.get('http://localhost:5000/data/fetch/users')
.then(response => {
dispatch(receiveUserLift(response))
})
.catch(error => console.log("FetchUserList Axios Error", error))
}
}
Reducer would include the following
case "RECEIVE_USERS":
return Object.assign({}, currentState, {
userList: action.payload
});
Component would include the following
import { fetchUserList } from "WHERE WE DECLARED OUR ARE ACTION";
class Example extends React.Component {
componentDidMount() {
// You could use it anywhere, but this is how you would call the action
this.props.fetch()
}
static getDerivedStateFromProps(nextProps, prevState) {
if (nextProps.users === prevState.users) return null;
return {
users: nextProps.users
};
}
}
const mapStateToProps = (state, ownProps) => {
return {
users: state.userList
};
};
const mapDispatchToProps = (dispatch, ownProps) => ({
fetch: () => dispatch(fetchUserList())
});
Sidenote
Receiving the data in our component asynchronously requires that we make use of component lifecycle methods. You will see a lot of guides around the internet advising to use componentWillReceiveProps to do this, but this is going to be removed in future versions of React and replaced with static getDerivedStateFromProps you can read more about it here
static getDerivedStateFromProps(nextProps, prevState) {
if (nextProps.users === prevState.users) return null;
return {
users: nextProps.users
};
}
The above code serves as a rough guide, but it is the general pattern that I've followed when dealing with Async API calls and rendering with Redux

Accessing a part of reducer state from one reducer within another reducer

I do not know how to access a boolean isLoading flag from reducerForm.js reducer in reducerRegister.js. I have used combineReducers() and I use isLoading to disable a button during form submit.
It's initial state is false, after clicking submit, it changes to true. After the form submission is successful, isLoading is reset to false again. Below is the relevant code for this issue:
actionRegister.js
let _registerUserFailure = (payload) => {
return {
type: types.SAVE_USER_FAILURE,
payload
};
};
let _registerUserSuccess = (payload) => {
return {
type: types.SAVE_USER_SUCCESS,
payload,
is_Active: 0,
isLoading:true
};
};
let _hideNotification = (payload) => {
return {
type: types.HIDE_NOTIFICATION,
payload: ''
};
};
// asynchronous helpers
export function registerUser({ // use redux-thunk for asynchronous dispatch
timezone,
password,
passwordConfirmation,
email,
name
}) {
return dispatch => {
axios.all([axios.post('/auth/signup', {
timezone,
password,
passwordConfirmation,
email,
name,
is_Active: 0
})
// axios.post('/send', {email})
])
.then(axios.spread(res => {
dispatch(_registerUserSuccess(res.data.message));
dispatch(formReset());
setTimeout(() => {
dispatch(_hideNotification(res.data.message));
}, 10000);
}))
.catch(res => {
// BE validation and passport error message
dispatch(_registerUserFailure(res.data.message));
setTimeout(() => {
dispatch(_hideNotification(res.data.message));
}, 10000);
});
};
}
actionForm.js
export function formUpdate(name, value) {
return {
type: types.FORM_UPDATE_VALUE,
name, //shorthand from name:name introduced in ES2016
value
};
}
export function formReset() {
return {
type: types.FORM_RESET
};
}
reducerRegister.js
const INITIAL_STATE = {
error:{},
is_Active:false,
isLoading:false
};
const reducerSignup = (state = INITIAL_STATE , action) => {
switch(action.type) {
case types.SAVE_USER_SUCCESS:
return { ...state, is_Active:false, isLoading: true, error: { register: action.payload }};
case types.SAVE_USER_FAILURE:
return { ...state, error: { register: action.payload }};
case types.HIDE_NOTIFICATION:
return { ...state , error:{} };
}
return state;
};
export default reducerSignup;
reducerForm.js
const INITIAL_STATE = {
values: {}
};
const reducerUpdate = (state = INITIAL_STATE, action) => {
switch (action.type) {
case types.FORM_UPDATE_VALUE:
return Object.assign({}, state, {
values: Object.assign({}, state.values, {
[action.name]: action.value,
})
});
case types.FORM_RESET:
return INITIAL_STATE;
// here I need isLoading value from reducerRegister.js
}
return state;
};
export default reducerUpdate;
reducerCombined.js
import { combineReducers } from 'redux';
import reducerRegister from './reducerRegister';
import reducerLogin from './reducerLogin';
import reducerForm from './reducerForm';
const rootReducer = combineReducers({
signup:reducerRegister,
signin: reducerLogin,
form: reducerForm
});
export default rootReducer;
This is where I use isLoading:
let isLoading = this.props.isLoading;
<FormGroup>
<Col smOffset={4} sm={8}>
<Button type="submit" disabled={isLoading}
onClick={!isLoading ? isLoading : null}
>
{ isLoading ? 'Creating...' : 'Create New Account'}
</Button>
</Col>
</FormGroup>
Mapping state to props within the same component
function mapStateToProps(state) {
return {
errorMessage: state.signup.error,
isLoading: state.signup.isLoading,
values: state.form.values
};
}
This is covered in the Redux FAQ at https://redux.js.org/faq/reducers#how-do-i-share-state-between-two-reducers-do-i-have-to-use-combinereducers:
Many users later want to try to share data between two reducers, but find that combineReducers does not allow them to do so. There are several approaches that can be used:
If a reducer needs to know data from another slice of state, the state tree shape may need to be reorganized so that a single reducer is handling more of the data.
You may need to write some custom functions for handling some of these actions. This may require replacing combineReducers with your own top-level reducer function. You can also use a utility such as reduce-reducers to run combineReducers to handle most actions, but also run a more specialized reducer for specific actions that cross state slices.
Async action creators such as redux-thunk have access to the entire state through getState(). An action creator can retrieve additional data from the state and put it in an action, so that each reducer has enough information to update its own state slice.
A reducer cannot access another reducer's state, but if you're using redux-thunk you can do so from within an action creator. As an example, you can define an action creator like this:
export const someAction = () =>
(dispatch, getState) => {
const someVal = getState().someReducer.someVal;
dispatch({ type: types.SOME_ACTION, valFromOtherReducer: someVal });
};
React Redux works on unidirectional data flow.
Action ---> Reducer /store ---> Reducer
Reducer works on small subset of store, you can not access store inside reducer which is not part of Reducer. you can either need to fire new action from the component based on reducer state return.

Categories