-
-
Notifications
You must be signed in to change notification settings - Fork 3.3k
Description
Do you want to request a feature or report a bug?
Bug, possibly.
(If this is a usage question, please do not post it here—post it on Stack Overflow instead. If this is not a “feature” or a “bug”, or the phrase “How do I...?” applies, then it's probably a usage question.)
What is the current behavior?
A component's useSelector
value is not updated when an action is fired from a utility function that changes that state. The action that is fired is clearly visible (and successful) in the Redux state timeline.
See discussion and code sample below; unable so far to get a sandox demo running... sorry.
If the current behavior is a bug, please provide the steps to reproduce and if possible a minimal demo of the problem. Your bug will get fixed much faster if we can run your code and it doesn't have dependencies other than React. Paste the link to a CodeSandbox (https://codesandbox.io/s/new) or RN Snack (https://snack.expo.io/) example below:
What is the expected behavior?
Since useSelector
is subscribing to changes to it's values, its value should be updated upon the change.
Which versions of React, ReactDOM/React Native, Redux, and React Redux are you using? Which browser and OS are affected by this issue? Did this work in previous versions of React Redux?
This is a new use case for me, so cannot compare to previous versions.
Redux : 4.0.4
React : 16.12.0
React-Redux : 7.1.1
(FYI: already posted on SO:
Discussion
I have several components that use a Modal to upload files and then submit them for processing by our backend. The user may upload one or more files and they are stored in Redux and then listed on the main page. The submission to the backend is done by a utility function which issues the API calls and checks status to ensure that the backend processing was correctly submitted. Each file submitted to the backend is identified by a groupId. The upload and subsequent submission is kicked off when the user clicks the "CONTINUE" button on the FileDrop component.
This was all working just fine until the requirement came along to exclude any duplicate files (by fileName for now). If a duplicate file is detected (by querying the list of files in the Redux store), the user is offered the opportunity to cancel the request or replace the existing file... and that's where the trouble is.
Here's a skeleton of the code, showing the relevant pieces. Cancelling a request works just fine; but I'm unable to correctly update the Redux store when replacing a file.
The flow through the code is as follows:
[1] User requests a file upload and component ModalFileDropPage
is invoked
[2] The FileDrop
component is invoked; the modal is displayed, the user selects a file, and clicks the "CONTINUE" button.
[3] The handleModalUpload
function is invoked, which submits the uploaded file to the backend. It is passed the function removeGroupIdFromRedux
as a parameter.
[4] If the file is a duplicate and the user requests that it replace the previous file, we delete the file from the backend and then request the associated Redux item be removed via the link to the removeGroupIdFromRedux
function. The current request for the uploaded file is handled in the normal fashion, returning to continueToNextPage
with the mediaInfo
(or possibly an error)
// The main component that requests the file upload and submits it for processing
import React from 'react';
// [1]
const ModalFileDropPage = props => {
const logos = useSelector(state => state.files.logos);
// [4]
const removeGroupIdFromRedux = async id => {
await dispatch(storeLogos([...logos.filter(({ groupId }) => groupId !== id)]));
};
const continueToNextPage = async currentPage => {
setMediaRoleId(mediaRoles);
// [3]
const mediaInfo = await handleModalUpload();
if (!mediaInfo.response) { // update redux if no errors
await dispatch(storeLogos([...logos, {
// SUCCESS: store uploaded file info in Redux
}]));
} else {
// ERROR: clear file data and cancel FileDrop modal
}
};
// [2]
return (
<FileDrop>
<Button onClick={event => continueToNextPage(1, event)}>CONTINUE</Button>
</FileDrop>
);
};
// from the separate file containing handleModalUpload utility function
const handleModalUpload = async (
file, // file name being processed
allFiles, // function to provide all files data from Redux
removeGroupFromRedux, // function to delete original file from Redux
setUploading, // Uploading state function
) => {
setUploading(true);
try {
/* Test for duplicate fileName and prompt user to either:
* 1) cancel the upload
* 2) replace the original file with the new copy of the file
*/
const replace = swal('cancel or replace', { buttons : true }); // uses SweetAlert
} catch (err) {
if (!replace) {
throw new DuplicateFileName('Duplicate file name'); // this works just fine
} else {
const success = await deleteMediaAsset(oldFile.groupId); // Delete file from backend
if (!success) {
throw new ReplaceDeleteError('Unable to delete existing file for replacement');
}
// Excerpt from the reducers...
import { filesActions } from '../actions/actionTypes';
import filesInitialState from '../../components/Files/initialState';
export default function reducer(state = filesInitialState, action) {
switch (action.type) {
case filesActions.STORE_LOGOS: {
return {
...state,
logos : action.payload
};
}
case filesActions.STORE_LOGOS_PAGE: {
return {
...state,
logosPage : action.payload
};
}
case filesActions.STORE_LOGOS_URL: {
const { logos, logosPageCurrentGroup } = state;
const logoIndex = logos.findIndex(item => item.groupId === logosPageCurrentGroup);
const logo = logos[logoIndex];
logo.url = action.payload;
logos[logoIndex] = logo;
return {
...state,
logos
};
}
default:
return state;
}
When the user opts to replace the existing file, the removeGroupIdFromRedux function is successfully called and the log shows that the action was successfully performed and there is now one less item in Redux (in the image below, there was only one file for testing, so the result from this call is an empty logos array).
The next thing that happens is that the SUCCESS code in continueToNextPage is executed and it sets the logos array to two items -- the original one and the newly-added one. The previous update to the Redux store is not recognized.
Thanks