javascript - 与REDUX Action 和 reducer 混淆

标签 javascript reactjs redux

因此,我试图从上一个问题中重构一些代码:

React: How to update one component, when something happens on another component

因此,我开始深入研究现有的代码模板,以了解其实现方式。

我找到了reducers.js,在其中添加了新的reducer:ActiveTenant

import Auth from './auth/reducer';
import App from './app/reducer';
import ThemeSwitcher from './themeSwitcher/reducer';
import LanguageSwitcher from './languageSwitcher/reducer';
import ActiveTenant from './activetenant/reducer';

export default {
  Auth,
  App,
  LanguageSwitcher,
  ThemeSwitcher,
  ActiveTenant
};


该新的减速器是这样的:

import { Map } from 'immutable';
import actions from './actions';
import { adalApiFetch } from '../../adalConfig';

const initState = new Map({
    tenantId: ''
});

export default function(state = initState, action) {
  switch (action.type) {
    case actions.SET_TENANT_ACTIVE:
    {
        const options = { 
            method: 'post'
        };

        adalApiFetch(fetch, "/Tenant/SetTenantActive?TenantName="+state.tenantId, options)
        .then(response =>{
            if(response.status === 200){
                console.log("Tenant activated");
            }else{
                throw "error";
            }
        })
        .catch(error => {
            console.error(error);
        });

        return state.set('tenant', state.Name);
    }
    default:
      return state;
  }
}


减速器的动作

const actions = {
  SET_TENANT_ACTIVE: 'SET_TENANT_ACTIVE',
  setTenantActive: () => ({
    type: actions.SET_TENANT_ACTIVE
  }),
};
export default actions;


然后,从组件本身,需要在前端选择一行时调用该动作,因此我已将注释的代码重构为一行。

import React, { Component } from 'react';
import {  Table, Radio} from 'antd';
import { adalApiFetch } from '../../adalConfig';
import Notification from '../../components/notification';
import actions from '../../redux/activetenant/actions';

const { setTenantActive } = actions;

class ListTenants extends Component {

    constructor(props) {
        super(props);
        this.state = {
            data: []
        };
    }



    fetchData = () => {
        adalApiFetch(fetch, "/Tenant", {})
          .then(response => response.json())
          .then(responseJson => {
            if (!this.isCancelled) {
                const results= responseJson.map(row => ({
                    key: row.id,
                    TestSiteCollectionUrl: row.TestSiteCollectionUrl,
                    TenantName: row.TenantName,
                    Email: row.Email
                  }))
              this.setState({ data: results });
            }
          })
          .catch(error => {
            console.error(error);
          });
      };


    componentDidMount(){
        this.fetchData();
    }

    render() {
        const columns = [
                {
                    title: 'TenantName',
                    dataIndex: 'TenantName',
                    key: 'TenantName',
                }, 
                {
                    title: 'TestSiteCollectionUrl',
                    dataIndex: 'TestSiteCollectionUrl',
                    key: 'TestSiteCollectionUrl',
                }, 
                {
                    title: 'Email',
                    dataIndex: 'Email',
                    key: 'Email',
                }
        ];

        // rowSelection object indicates the need for row selection
        const rowSelection = {
            onChange: (selectedRowKeys, selectedRows) => {
                if(selectedRows[0].TenantName != undefined){
                    console.log(selectedRows[0].TenantName);
                    const options = { 
                        method: 'post'
                    };

                    setTenantActive(selectedRows[0].TenantName);
                    /* adalApiFetch(fetch, "/Tenant/SetTenantActive?TenantName="+selectedRows[0].TenantName.toString(), options)
                        .then(response =>{
                        if(response.status === 200){
                            Notification(
                                'success',
                                'Tenant set to active',
                                ''
                                );
                        }else{
                            throw "error";
                        }
                        })
                        .catch(error => {
                        Notification(
                            'error',
                            'Tenant not activated',
                            error
                            );
                        console.error(error);
                    }); */
                }
            },
            getCheckboxProps: record => ({
                type: Radio
            }),
        };

        return (
            <Table rowSelection={rowSelection} columns={columns} dataSource={this.state.data} />
        );
    }
}

export default ListTenants;


但是,对我来说,动作和reducer之间的关系还不清楚,如果我检查调试器,则该动作已执行,并且未接收到任何参数,但从未执行过reducer。

我是否必须在某个地方派遣产品?这个难题中我缺少什么?

最佳答案

所以首先要了解的是Redux Cycle:

动作创建者->动作->调度->减速器->状态

动作创建者:动作创建者是一个函数,该函数将创建或返回一个普通的JavaScript对象,该对象称为Action,具有type属性和payload属性,该属性描述您要对数据进行的某些更改。

payload属性描述了有关我们要进行的更改的一些上下文。

动作的目的是描述应用程序内部数据的某些更改。

动作创建者将创建动作。

dispatch函数将接受一个Action并制作该对象的副本,并将其传递到应用程序内部的多个不同位置,这些位置将我们引向Reducers。

在Redux中,reducer是负责执行Action的功能。它要处理该Action,对数据进行一些更改并返回它,以便可以将其集中在某个位置。

在Redux中,State属性是我们的化简器产生的所有信息的中央存储库。所有信息都被整合到State对象中,因此我们的React应用程序可以轻松地进入应用程序的Redux端并访问应用程序内部的所有数据。

因此,通过这种方式,该应用程序不必遍历每个单独的reducer并询问当前状态。

因此,将其消化几分钟,然后查看您的体系结构。

让我们跳过减速器。

用动作创建者创建的动作调用reducers。减速器将查看该动作并决定是否需要基于该动作来修改某些数据。

因此,换句话说,Reducer的工作不是执行API请求,而是处理动作创建者发送给它的动作。

所以代替这个:

import { Map } from 'immutable';
import actions from './actions';
import { adalApiFetch } from '../../adalConfig';

const initState = new Map({
    tenantId: ''
});

export default function(state = initState, action) {
  switch (action.type) {
    case actions.SET_TENANT_ACTIVE:
    {
        const options = { 
            method: 'post'
        };

        adalApiFetch(fetch, "/Tenant/SetTenantActive?TenantName="+state.tenantId, options)
        .then(response =>{
            if(response.status === 200){
                console.log("Tenant activated");
            }else{
                throw "error";
            }
        })
        .catch(error => {
            console.error(error);
        });

        return state.set('tenant', state.Name);
    }
    default:
      return state;
  }
}


减速器应如下所示:

import { SET_TENANT_ACTIVE } from "../actions/types";

const initialState = {
    tenantId: ''
};

export default (state = initialState, action) {
  switch (action.type) {
    case SET_TENANT_ACTIVE:
      return {...state, [action.payload.id]: action.payload };
    default:
      return state;
  }
}


然后,在动作创建者文件中,应该有一个类似于以下内容的动作创建者:

import axios from 'axios';
import { SET_TENANT_ACTIVE } from "../actions/types";


export const setTenant = id => async (dispatch) => {
  const response = await axios.post(`/tenants/${id}`);

  dispatch({ type: SET_TENANT_ACTIVE, payload: response.data });
};


您还需要了解Redux项目的结构,因为在上述重构之后,您将丢失如何将所有这些连接到组件的方法。在您的组件文件中,没有connect()函数,该函数也需要Provider标记,而您没有。

因此,为此,我建议您首先设置文件夹和文件结构,如下所示:

  /src
    /actions
    /components
    /reducers
    index.js


因此,在您的index.js文件中,其外观应如下所示:

import React from "react";
import ReactDOM from "react-dom";
import { Provider } from "react-redux";
import { createStore, applyMiddleware, compose } from "redux";
import reduxThunk from "redux-thunk";

import App from "./components/App";
import reducers from "./reducers";

const composeEnhancers = window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__ || compose;
const store = createStore(
  reducers,
  composeEnhancers(applyMiddleware(reduxThunk))
);

ReactDOM.render(
  <Provider store={store}>
    <App />
  </Provider>,
  document.querySelector("#root")


因此,这里的目标是确保在组件层次结构的最顶层获得该Provider标记,并确保将其传递给Redux商店的引用,以将所有reducer加载到其中。

因此,在上面我已经创建了商店,并将其传递给我们一组减速器,它将返回给您所有应用程序状态。

最后,您在上面看到的是我创建了<Provider>的实例,并用它包装了<App />组件,然后您要传递<Provider>组件的是一个称为store的道具。 store是调用createStore()并调用reducers的结果。

<Provider>是代表我们与Redux store进行交互的内容。

请注意,我还连接了J. Hesters提到的Redux-Thunk,据我从您的代码中可以看到,您正在发出ajax请求,这就是为什么我为您提供了一个异步动作创建者,这意味着您将需要Redux- Thunk或类似的中间件,让我不要冒犯Redux-Saga粉丝,因此至少您有这两种选择。您对Redux似乎比较陌生,只需使用Redux-Thunk。

现在,您可以使用组件文件中的connect()组件来完成将这些动作创建者和减速器连接到组件或应用程序的React端的工作。

import React, { Component } from 'react';
import { connect } from "react-redux";
import {  Table, Radio} from 'antd';
import { adalApiFetch } from '../../adalConfig';
import Notification from '../../components/notification';
import actions from '../../redux/activetenant/actions';


导入connect之后,您可以在下面创建一个实例:

export default connect()(ListTenants);


请不要就上述语法与我争论(实际上有位前学生向我报告了管理员使用此语法作为不知道我在做什么的证据)。

然后,如果需要,您需要通过添加connect()来配置此mapStateToProps React组件,但必须将actions作为第二个参数传递给connect()。如果您意识到不需要mapStateToProps,则只需将null作为第一个参数传递,但不能将其保留为空。

希望所有这些对您有所帮助,并欢迎您进入React-Redux的美好世界。

关于javascript - 与REDUX Action 和 reducer 混淆,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/54921500/

相关文章:

react-native - react 原生 redux 连接未定义的函数

javascript - redux-thunk 将调度传递给另一个函数?

javascript - 等待 async firebase foreach?

javascript - 如何根据相似度更改数组中对象的参数?

javascript - D3 v4 - 获取当前缩放比例?

javascript - react : issue with the search and filter function

reactjs - 针对 Twitch 扩展的 React redux 操作

javascript - 如何在 Redux/React 应用程序中加载数据 "on-demand"

javascript - Google Plus 按钮在 magento 中不起作用

javascript - 如何检索 "it" block 状态并根据结果我需要在 ALM 中上传屏幕截图