【问题标题】:React: integrating a redux reducer to dandelion-pro projectReact:将 redux reducer 集成到 dandelion-pro 项目中
【发布时间】:2021-01-07 19:01:37
【问题描述】:

终端开发人员,最近我开始学习前端。我在向 redux 商店添加一些新数据时遇到了麻烦。我正在使用 dandelion-pro react 模板并且无法弄清楚如何将我的减速器添加到他们的商店,它似乎比我为其他项目构建的 redux 商店复杂得多,我也观察到他们使用了 redux saga。我正在尝试为登录时的用户数据引入全局状态。

这是我的减速器的代码

import { CallToAction } from '@material-ui/icons';
import { SUCCESSFUL_LOGIN, FETCH_LOGIN, ERROR_LOGIN } from '../../actions/actionConstants';

const initialState = {
    auth: false,
    isLoading: false,
    errMess: null,
    isAdmin: false,
    token: ''
}

export default function userReducer (state = initialState, action) {
    console.log("Action: ")
    console.log(action)
    switch (action.type) {
        case SUCCESSFUL_LOGIN: return {
            ...state,
            auth: true,
            isLoading: false,
            errMess: null,
            isAdmin: action.payload.isAdmin,
            token: action.payload.token
        }
        case FETCH_LOGIN: return {
            ...state,
            auth: false,
            isLoading: true,
            errMess: null
        }
        case ERROR_LOGIN: return {
            ...state,
            auth: false,
            isLoading: false,
            errMess: action.payload
        }
        default: return state
    }
}

获取用户数据的代码

import { SUCCESSFUL_LOGIN, FETCH_LOGIN, ERROR_LOGIN } from '../../actions/actionConstants';
import axios from 'axios';
import { server } from '../../config'

export const fetchUser = (username, password) => (dispatch) => {
    
    console.log("a ajuns")
    dispatch(loginLoading(true));

    axios.post(`${server + "/auth/login"}`, { username, password })
        .then(res => {
            const user = res.data;
            console.log(user);

            if (user.status) {
                window.location.href = '/app';
                return dispatch(loginUser(user));
            }
            else {
                var errmess = new Error("False Status of User");
                throw errmess;
            }

      })
      .catch(error => dispatch(loginFailed(error.message)))
}

export const loginLoading = () => ({
    type: FETCH_LOGIN
});

export const loginFailed = (errmess) => {
    return ({
        type: ERROR_LOGIN,
        payload: errmess
    })
};

export const loginUser = (user) => ({
    type: SUCCESSFUL_LOGIN,
    payload: user
})

组合减速器的部分

/**
 * Combine all reducers in this file and export the combined reducers.
 */
import { reducer as form } from 'redux-form/immutable';
import { combineReducers } from 'redux-immutable';
import { connectRouter } from 'connected-react-router/immutable';
import history from 'utils/history';

import languageProviderReducer from 'containers/LanguageProvider/reducer';
import login from './modules/login';
import uiReducer from './modules/ui';
import initval from './modules/initForm';
import user from '../my_redux/modules/initForm';

/**
 * Creates the main reducer with the dynamically injected ones
 */
export default function createReducer(injectedReducers = {}) {
  const rootReducer = combineReducers({
    user,
    form,
    login,
    ui: uiReducer,
    initval,
    language: languageProviderReducer,
    router: connectRouter(history),
    ...injectedReducers,
  });

  // Wrap the root reducer and return a new root reducer with router state
  const mergeWithRouterState = connectRouter(history);
  return mergeWithRouterState(rootReducer);
}

我尝试像这样连接我的登录组件

const mapStateToProps = state => ({
  user: state.user
});

const mapDispatchToProps = dispatch => ({
  fetchUser: (username, password) => dispatch(fetchUser(username, password))
});

// const mapDispatchToProps = dispatch => ({
//   actions: bindActionCreators(userActions, dispatch),
// });

export default withStyles(styles)(connect(mapStateToProps, mapDispatchToProps)(Login));

商店在这里创建

/**
 * Create the store with dynamic reducers
 */

import { createStore, applyMiddleware, compose } from 'redux';
import { routerMiddleware } from 'connected-react-router';
import { fromJS } from 'immutable';
import createSagaMiddleware from 'redux-saga';
import createReducer from './reducers';

export default function configureStore(initialState = {}, history) {
  let composeEnhancers = compose;
  const reduxSagaMonitorOptions = {};

  // If Redux Dev Tools and Saga Dev Tools Extensions are installed, enable them
  /* istanbul ignore next */
  if (process.env.NODE_ENV !== 'production' && typeof window === 'object') {
    /* eslint-disable no-underscore-dangle */
    if (window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__) composeEnhancers = window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__({});

    // NOTE: Uncomment the code below to restore support for Redux Saga
    // Dev Tools once it supports redux-saga version 1.x.x
    // if (window.__SAGA_MONITOR_EXTENSION__)
    //   reduxSagaMonitorOptions = {
    //     sagaMonitor: window.__SAGA_MONITOR_EXTENSION__,
    //   };
    /* eslint-enable */
  }

  const sagaMiddleware = createSagaMiddleware(reduxSagaMonitorOptions);

  // Create the store with two middlewares
  // 1. sagaMiddleware: Makes redux-sagas work
  // 2. routerMiddleware: Syncs the location/URL path to the state
  const middlewares = [sagaMiddleware, routerMiddleware(history)];

  const enhancers = [applyMiddleware(...middlewares)];

  const store = createStore(
    createReducer(),
    fromJS(initialState),
    composeEnhancers(...enhancers),
  );

  // Extensions
  store.runSaga = sagaMiddleware.run;
  store.injectedReducers = {}; // Reducer registry
  store.injectedSagas = {}; // Saga registry

  // Make reducers hot reloadable, see http://mxs.is/googmo
  /* istanbul ignore next */
  if (module.hot) {
    module.hot.accept('./reducers', () => {
      store.replaceReducer(createReducer(store.injectedReducers));
    });
  }

  return store;
}

在登录表单提交时,我调用this.props.fetchUser("admin", "admin");,但出现以下错误:

Uncaught Error: Actions must be plain objects. Use custom middleware for async actions.
    at dispatch (redux.js:198)
    at eval (middleware.js:29)
    at eval (redux-saga-core.dev.cjs.js:1412)
    at Object.fetchUser (Login.js?f3c5:66)
    at Login.submitForm (Login.js?f3c5:30)
    at onSubmit (Login.js?f3c5:49)
    at executeSubmit (handleSubmit.js?e3b3:39)
    at handleSubmit (handleSubmit.js?e3b3:131)
    at Form._this.submit (createReduxForm.js?d100:362)
    at HTMLUnknownElement.callCallback (react-dom.development.js:149)

【问题讨论】:

    标签: reactjs redux react-redux frontend redux-saga


    【解决方案1】:

    我查看了我的答案,并根据您的问题更新进行更新

    您用于定义async 函数的语法称为thunk 一个返回承诺(或异步函数)的函数的花哨名称,无论如何要在代码中使用该模式,您需要一个名为redux-thunk 的库

    要为您的应用程序应用 redux-thunk 中间件,

    npm install redux-thunk
    

    然后在您的应用商店中应用中间件

    import { createStore, applyMiddleware } from 'redux';
    import thunk from 'redux-thunk';
    import rootReducer from './reducers/index';
    
    // Note: this API requires redux@>=3.1.0
    const store = createStore(rootReducer, applyMiddleware(thunk));
    

    来自redux-thunk官方repo的例子

    对于您的代码,只需在中间件数组中添加从 redux-thunk 导入的 thunk

      import thunk from 'redux-thunk';
    
      const middlewares = [sagaMiddleware, routerMiddleware(history), thunk];
    

    现在是佐贺

    你需要有一个运行其他 saga 的 root saga,并从创建的 saga 中间件运行 root saga

    步骤如下:

    1- 创建 saga 中间件(就像你做的那样,但我们也需要从那里运行 root saga)

    import createSagaMiddleware from 'redux-saga'
    
    const sagaMiddleware = createSagaMiddleware();
    
    // after you've created the store then run the root saga
    sagaMiddleware.run(rootSagas);
    

    2- 创建你的 rootSaga

    export function* rootSagas() {
    
        try {
    
            yield fork(fetchUsersSaga);
    
    
        } catch (error) {
            console.warn(error);
        }
    }
    

    3- 创建您的 fetch 用户传奇

    import { take, put, call } from "redux-saga/effects";
    
    export function* fetchUsersSaga() {
    
        while (true) {
            const action: FetchUser = yield take(FETCH_USER);
    
            try {
    
                const response = yield call(usersService.fetchUsersOfProject, { ...paramsPassedToFetchUserFunction })
    
                if (response) {
                    const { data: { response: { user } } } = response;
                    yield put(setUser({ user }));
                }
    
    
            } catch (error) {
                yield put(fetchUser());
            }
        }
    }
    

    现在您需要注意 saga 和 thunk 之间的巨大区别,因为 thunk 您编写了一个硬编码的动作来做一件事(或多个,但它仍然适用于更具体的情况),而在 saga 中您会听什么商店已调度的操作并以generator 代码样式对该操作做出反应

    【讨论】:

    • 商店已创建,但在另一个文档中。我会更新我的问题。
    猜你喜欢
    • 2023-03-11
    • 2018-07-01
    • 2017-11-06
    • 1970-01-01
    • 1970-01-01
    • 2021-04-26
    • 2012-03-05
    • 2019-02-15
    • 2018-07-30
    相关资源
    最近更新 更多