My component has a check box that will fire off a this.props.toggleChange action creator when clicked. In the toggleChange action creator, it will dispatch a TOGGLE_CONFIG_CHANGE, which is picked up by a Redux Saga. The inside of the saga function looks like this:
export function* toggledConfigSaga () {
yield put(toggleConfigClicked()); // this fires off TOGGLE_CONFIG_CLICKED
yield put(saveAllCurrentConfigsAndFetchServerStuff()); // this function will throw up a spinner as asynch actions complete
}
In my reducer, I have CASE types.TOGGLE_CONFIG_CLICKED: return {...state, toggleConfig: !state.configChecked};. In redux dev tools and console log the configClicked boolean is changed instantaneously on check box click(as one would expect). However, the check mark doesn't toggle until the asynch actions completes, giving it the illusion of a laggy button. What gives?
Related
I am trying to get my head around a scenario where I am dispatching a synchronous redux action (using createAction of typesafe-actions) and soon after that making a network call that relies on updated props from the store.
Scenario:
Inside clearFilters handler function (handler function invoked on click of clear filters button), I am dispatching a synchronous action and then making a network call as below:
clearFilters = (): void => {
this.props.resetFilters(); //action dispatched
this.refreshData; //network call
};
Inside the refreshData function, my component expects updated filter props and based on it, it creates a searchCondition to be passed to the list api call as payload.
refreshData = (): void => {
const { listData, filters } = this.props; //get the filters prop
//Expected filters to be updated from the store post dispatch of action
const SearchCondition: SearchCondition = createSearchConditions(filters);
listData({
SearchCondition,
MaxResults: this.maxRecordsCount,
SortFields: this.getSortFields(),
}),
);
};
My component is subscribed to the filters prop using mapStateToProps:
const mapStateToProps = (state: RootState) => ({
filters: state.common.filter.filters,
});
Given that is the state of the problem I am facing, I tried to debug what happens by placing debug points in the code:
When the action is dispatched (inside clearFilters function)
Inside the reducer, where updated state is returned.
When the network call is invoked (inside clearFilters function)
In the refreshData call.
After reducer returns updated state, as per the debugging knowledge, store did not send the updated props right away. Rather, the control goes back to the next line i.e. this.refreshData() which make network call with old filters data. Only after the clearFilters function call finishes, in my componentDidUpdate, i can see that props update happen.
Does that signifies redux state change back to the store and eventually subscribed prop updates happen in an ASYNC way? If so, how does it happen? Does store sending the updated props executes in the main thread?
Any pointers/documentation would be really helpful.
The dispatch is synchronous, and the queueing of the React updates is synchronous. However, React will not re-render that component until after this whole event processing is completed, and this.props will not be updated until after that render happens. So, no, you cannot access this.props right after dispatching an action and expect that it has been updated. That will never be true.
I would suggest reading these posts that go into extensive detail on both React and React-Redux:
A (Mostly) Complete Guide to React Rendering Behavior
The History and Implementation of React-Redux
I want to cancel some functions after component unmount because it causes memory leak my code looks like this
componentDidUpdate(prevProps) {
if (prevProps.org.org !== this.props.org.org && this.mounted) {
this.props.clearGraph();
this.props.graphGet(this.props.org.org);
this.setState({ org: this.props.org.org });
}
}
componentDidMount() {
const abox = "a" + this.props.org.org.substr("1");
this.props.getHistory(abox);
this.props.graphGet(this.props.org.org);
}
componentWillUnmount() {
}
all I want is to cancel graphGet which is a redux action
You cannot cancel Redux actions by design. Once they get dispatched, they are processed by reducers and the state transition is performed.
You can however dispatch another action to revert the state changes or causing side effects. Normally you would use another library like redux-observable for the latter.
You can for example define 3 actions: START_GRAPH_GET, CANCEL_GRAPH_GET and FINISH_GRAPH_GET. On START you start your fetch, on CANCEL you cancel any outstanding fetches and once a fetch completes you dispatch FINISH and keep the result in the store.
In order to render the results you would need to use react-redux connect with a mapStateToProps function.
To cancel on unmount, you would just dispatch an CANCEL action, if necessary.
Since your code does not show anything related to Redux at all, I think a more general answer is reasonable here.
How do I execute custom callback that is passed into an action through react comp, immediately after redux store update.
The idea is say, I trigger an action from react, which will make network request via thunk and dispatches the action with data. This will lead to reducer updating the store. Now, immediately after this I want to redirect to a different page (history.push()) which is a callback.
Using saga middleware it is much easier, but how to implement similar functly using thunk.
You can pass your callback defined in your component the redirect to different page to the thunk and call that after store update is complete. Like this:
function someThunkAction(callback) {
return (dispatch, getState) => {
// Update store logic...
// After update
callback();
};
}
I want to cancel some functions after component unmount because it causes memory leak my code looks like this
componentDidUpdate(prevProps) {
if (prevProps.org.org !== this.props.org.org && this.mounted) {
this.props.clearGraph();
this.props.graphGet(this.props.org.org);
this.setState({ org: this.props.org.org });
}
}
componentDidMount() {
const abox = "a" + this.props.org.org.substr("1");
this.props.getHistory(abox);
this.props.graphGet(this.props.org.org);
}
componentWillUnmount() {
}
all I want is to cancel graphGet which is a redux action
You cannot cancel Redux actions by design. Once they get dispatched, they are processed by reducers and the state transition is performed.
You can however dispatch another action to revert the state changes or causing side effects. Normally you would use another library like redux-observable for the latter.
You can for example define 3 actions: START_GRAPH_GET, CANCEL_GRAPH_GET and FINISH_GRAPH_GET. On START you start your fetch, on CANCEL you cancel any outstanding fetches and once a fetch completes you dispatch FINISH and keep the result in the store.
In order to render the results you would need to use react-redux connect with a mapStateToProps function.
To cancel on unmount, you would just dispatch an CANCEL action, if necessary.
Since your code does not show anything related to Redux at all, I think a more general answer is reasonable here.
Lets say i have a form where user is about to click on combination of buttons.
Each button triggers an action of type T and reducer R then updates its state and new combination is rendered on a website.
Now comes the tricky part:
I have my business logic implemented in reducer which applies new state which is about to be rendered. What i need now is when that state accepts a condition, i want to dispatch new action (api request).
What is the right approach to accomplish this kind of problem?
Set a flag into state, and call new action in component after?
Somehow dispatch a function in reducer?
...?
Redux Thunk allows you to dispatch multiple actions and dispatch asynchronous actions inside your action creators. For your scenario, you can do something like this:
function myAction() {
return (dispatch, getState) => {
...
dispatch(firstAction);
const state = getState();
// Check your state conditions from first action here.
dispatch(secondAction);
...
}
}
In this case you could use library redux-saga.
Using redux-saga, you'll be able to create a saga that you call from a component like actions. In the saga you will be able to call several redux-actions. If your state will be valid from the saga, you can call the API request.
A popular alternative to redux-saga is also redux-thunk.