与REDUX操作和reducers混淆

ix0qys7i  于 12个月前  发布在  其他
关注(0)|答案(3)|浏览(159)

所以我尝试从我之前的问题中重构一些代码:
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
};

字符串
新的reducer是这样的:

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;
  }
}


和该reducer的操作

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;


然而,我不清楚action和reducer之间的关系,如果我检查调试器,action被执行,没有参数被接收,但reducer从未被执行。
我必须把一个调度的地方?,我在这个拼图失踪?

hfwmuf9z

hfwmuf9z1#

所以首先要理解的是Redux循环

Action Creator-->Action-->Dispatch-->Reducers-->State
动作创建者:action creator是一个函数,它将创建或返回一个普通的JavaScript对象,称为Action,带有type属性和payload属性,描述您想要对数据进行的更改。

payload属性描述了我们想要进行的更改的上下文。

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

Action Creator是创建Action的功能。
dispatch函数将接收一个Action,并复制该对象,然后将其传递到应用程序中的一系列不同位置,这将我们引向Reducers
在Redux中,reducer是一个负责接收Action的函数。它将处理该Action,对数据进行一些更改并返回数据,以便将其集中在某个位置。
在Redux中,State属性是reducers生成的所有信息的中央存储库。所有信息都合并在State对象中,因此我们的React应用程序可以轻松访问应用程序的Redux端,并访问应用程序内部的所有数据。
因此,通过这种方式,应用程序不必绕到每个单独的reducer并询问当前的State
所以花几分钟消化一下,然后看看你的架构。
让我们跳到reducers

Reducer通过Action Creator创建的Action调用,Reducer会查看该Action,并根据该Action决定是否需要修改某些数据。

所以换句话说,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;
  }
}

字符串
你的reducer应该看起来像这样:

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;
  }
}


然后,在您的action creators文件中,您应该有一个类似于以下内容的action creator:

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加载到它里面。
所以上面我已经创建了商店,并将我们的reducer集传递给它,它将返回给你所有的应用程序State
最后,你在上面看到的是,我创建了一个<Provider>的示例,并用它 Package 了<App />组件,然后你想传递<Provider>组件是一个名为store的单个prop。store是调用createStore()和调用reducers的结果。
<Provider>代表我们与Redux store进行交互。
注意,我还连接了J. Hesters提到的Redux-Thunk,从你的代码中我可以看到你正在发出一个异步请求,这就是为什么我为你提供了一个异步动作创建器,这意味着你需要Redux-Thunk或类似的中间件,让我不要冒犯Redux-Saga的粉丝,所以你至少有这两个选择。
现在你可以在组件文件中使用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);


请不要和我争论上面的语法(* 实际上有一个以前的学生向管理员报告我使用这种语法作为不知道我在做什么的证据 *)。
然后你需要通过添加mapStateToProps来配置这个connect() React组件,如果你需要的话,但是一定要传入actions作为connect()的第二个参数。如果你意识到你不需要mapStateToProps,那么就传入null作为第一个参数,但是你不能让它为空。

brqmpdu1

brqmpdu12#

你使用Reducer的方法是错误的。Reducer应该是纯的。你的Reducer有副作用,表明你还没有理解Redux。
而不是写下一个解决方案给你(这将永远无论如何,因为一个人将不得不解释Redux的总数),我建议你投资3个小时,通过Redux文档,并按照tutorials(他们是伟大的)。
然后你可能想看看Redux Thunk,但是你可能不需要thunks。
附言:(这是一件小事,但我还没有看到有人在Redux中使用Map s。你这样做有什么原因吗?你可能想使用普通对象。)

cnwbcb6i

cnwbcb6i3#

您的操作不正确,您应该将活动租户名称作为参数传递。
参考https://redux-starter-kit.js.org/api/createaction
我们可以在这两个地方都将action类型写成内联字符串。action creators很好,但它们不需要使用Redux -组件可以跳过提供mapDispatch参数来连接,只需调用this.props.dispatch({type:“CREATE_POST”,payload:{id:123,title:“Hello World”}})本身。
参考https://redux-starter-kit.js.org/usage/usage-guide

相关问题