Ответы пользователя по тегу Redux
  • Как работать с асинхронными функциями в reducer redux?

    TMProject
    @TMProject
    Frontend developer React/Redux
    Проблема заключается в том, что асинхронная функция img.onload выполняется после того, как функция-редюсер закончила свою работу, и к моменту выполнения img.onload объект state.canvas уже был обновлен и утрачен свою возможность использования. Это приводит к ошибке, которую вы видите.

    Для решения этой проблемы можно воспользоваться библиотекой Redux-Thunk, которая позволяет создавать асинхронные функции внутри функций-редюсеров. В вашем случае, вы можете заменить pushToUndo и pushToRedo на асинхронные функции, которые будут использовать dispatch для вызова функций-редюсеров после завершения асинхронной операции.

    Пример использования Redux-Thunk для вашего случая:
    import { createSlice, createAsyncThunk } from '@reduxjs/toolkit';
    
    export const loadImage = createAsyncThunk(
      'canvas/loadImage',
      async (dataURL, { getState }) => {
        const state = getState().canvas;
        const img = new Image();
        img.src = dataURL;
        return new Promise((resolve, reject) => {
          img.onload = () => {
            const ctx = state.canvas.getContext('2d');
            ctx.clearRect(0, 0, state.canvas.width, state.canvas.height);
            ctx.drawImage(img, 0, 0, state.canvas.width, state.canvas.height);
            resolve();
          };
          img.onerror = reject;
        });
      }
    );
    
    export const canvasSlice = createSlice({
      name: 'canvas',
      initialState: {
        canvas: null,
        undoList: [],
        redoList: [],
      },
      reducers: {
        setCanvas: (state, action) => {
          state.canvas = action.payload;
        },
        pushToUndo: (state, action) => {
          state.undoList.push(action.payload);
        },
        pushToRedo: (state, action) => {
          state.redoList.push(action.payload);
        },
        undo: (state) => {
          if (state.undoList.length) {
            const dataURL = state.undoList.pop();
            state.redoList.push(dataURL);
            dispatch(loadImage(dataURL));
          } else {
            console.log('Нет элементов для отката');
          }
        },
        redo: (state) => {
          if (state.redoList.length) {
            const dataURL = state.redoList.pop();
            state.undoList.push(state.canvas.toDataURL());
            dispatch(loadImage(dataURL));
          }
        },
      },
      extraReducers: (builder) => {
        builder.addCase(loadImage.pending, (state) => {
          // обработка начала загрузки изображения
        });
        builder.addCase(loadImage.fulfilled, (state) => {
          // обработка завершения загрузки изображения
        });
        builder.addCase(loadImage.rejected, (state) => {
          // обработка ошибки загрузки изображения
        });
      },
    });
    
    export const { setCanvas, pushToUndo, pushToRedo, undo, redo } = canvasSlice.actions;
    
    export default canvasSlice.reducer
    Ответ написан
    5 комментариев
  • Почему происходит рендер не ожидая ответа от сервера?

    TMProject
    @TMProject
    Frontend developer React/Redux
    У тебя в newsReducer и в newReducer используются одни и те же action.type, у тебя два ведь два разных стейта поэтому и разные должны быть типы экшенов. Например
    export const newsReducer = (state = initialState, action:DataAction): DataState =>{
        switch (action.type){
            case DataActionTypes.FETCH_DATA_NEWS:
                return {loading:true, error:null, data: []};
            case DataActionTypes.FETCH_DATA_NEWS_SUCCESS:
                return {loading:false, error:null, data: action.payload};
            case DataActionTypes.FETCH_DATA_NEWS_ERROR:
                return {loading:false, error:action.payload, data: []};  
    
            default:
                return state
        }
    
    }
    
    export const newReducer = (state = initialStateNew, action:DataActionNew): DataStateNew =>{
        switch (action.type){
            case DataActionTypes.FETCH_DATA_NEW:
                return {loading:true, error:null, data: []};
            case DataActionTypes.FETCH_DATA_NEW_SUCCESS:
                return {loading:false, error:null, data: action.payload};
            case DataActionTypes.FETCH_DATA_NEW_ERROR:
                return {loading:false, error:action.payload, data: []};  
    
            default:
                return state
        }
    
    }

    Далее соответственно эти типы нужно использовать при диспатче
    Ответ написан