es.davy.ai

Preguntas y respuestas de programación confiables

¿Tienes una pregunta?

Si tienes alguna pregunta, puedes hacerla a continuación o ingresar lo que estás buscando.

Descargue un archivo programáticamente mientras brinda retroalimentación al usuario utilizando Redux.

Tengo un backend que devuelve un archivo para descargar.

Actualmente, cuando el usuario realiza una acción, se envía una acción que ejecuta el reductor “export” y realiza el truco habitual para descargar un archivo. Estoy usando redux-toolkit.

const mySlice = createSlice({
    name: "my",
    initialState,
    reducers: {
        export: (state) => {
            const link = document.createElement("a")
            link.href = '...';

            document.body.appendChild(link)
            link.click()
            link.parentNode?.removeChild(link)
        },
    }
})

Y esto funciona. Sin embargo, me gustaría proporcionar un feedback al usuario mientras se está realizando la descarga, es decir, un diálogo modal. Así que he definido una propiedad “isExporting” en el estado, y hay un componente que muestra el diálogo dependiendo de esta propiedad.

Sin embargo, el reductor escrito de esta manera no funciona:

export: (state) => {
    state.isExporting = true;

    const link = document.createElement("a")
    link.href = '...';

    document.body.appendChild(link)
    link.click()
    link.parentNode?.removeChild(link)

    state.isExporting = false;
}

porque creo que estoy introduciendo efectos secundarios, y además el estado se establecerá después de que se haya ejecutado el reductor y no en medio.

¿Cuál es la forma correcta de hacer esto?

Tags:  , ,

Answer

  1. Avatar for davy.ai

    La forma correcta de hacer esto es utilizar una acción asíncrona en redux-toolkit. Esto se puede lograr creando un creador de acciones adicional exportAsync tipo thunk que realiza la descarga del archivo, mientras se establece isExporting como true antes de que comience la descarga y se vuelve a establecer como false después de que la descarga se completa. Aquí tienes un ejemplo de cómo implementarlo:

    const mySlice = createSlice({
      name: "my",
      initialState,
      reducers: {
        setIsExporting: (state, action) => {
          state.isExporting = action.payload;
        }
      },
      extraReducers: (builder) => {
        builder.addCase(exportAsync.pending, (state) => {
          state.isExporting = true;
        });
        builder.addCase(exportAsync.fulfilled, (state) => {
          state.isExporting = false;
        });
      }
    })
    
    export const exportAsync = createAsyncThunk("my/exportAsync", async (url) => {
      const link = document.createElement("a");
      link.href = url;
      document.body.appendChild(link);
      link.click();
      link.parentNode?.removeChild(link);
    });
    
    export default mySlice.reducer;
    

    En el código anterior, setIsExporting es una acción síncrona normal que establece isExporting en el estado, y extraReducers escucha las acciones pending y fulfilled de exportAsync (que serán despachadas automáticamente por el middleware thunk al invocarla). Cuando se despacha exportAsync.pending, isExporting se establece en true, y cuando se despacha exportAsync.fulfilled (es decir, cuando se completa la descarga), se vuelve a establecer en false.

    Para despachar exportAsync en un componente, simplemente impórtala e inclúyela como una propiedad o utilízala con useDispatch(), dependiendo de cómo estés utilizando Redux en tu aplicación:

    import { exportAsync } from "./mySlice";
    
    function MyComponent() {
      const dispatch = useDispatch();
    
      const handleClick = () => {
        dispatch(exportAsync("https://example.com/download"));
      };
    
      return (
        <div>
          {isExporting && <modal>Cargando...</modal>}
          <button onclick="{handleClick}">Exportar</button>
        </div>
      );
    }
    

    Ten en cuenta que este enfoque elimina la necesidad de la acción export en tu código original, ya que ahora está manejada completamente por exportAsync.

Comments are closed.