草庐IT

javascript - react redux 架构表操作和缩减器

coder 2024-05-08 原文

我在多个页面的表格中显示服务器驱动的数据。

我目前有下表操作

pageChange
pageSizeChange
sort
load
loaded

我在一些需要触发加载的页面上有过滤器。

我有多个使用此功能的实体,它们将共享上述大部分逻辑,但需要单独的加载功能定义。

我的想法是采取将表 ID 作为参数的操作,然后具有一个 createTableReducer 函数,该函数也将采用此 ID 并将表节点挂载到实体中,类似于 react-redux-form 中的 createModelReducer|

如果不使用像 redux saga 这样的东西,我怎么能从我的通用操作中触发实体特定的加载操作?

我想知道如何创建一个高阶组件并将其传递给加载函数,但我认为这不会解决我的问题。我也可以从表组件本身调用更改操作和加载操作,但这似乎不是一个好的解决方案。

最佳答案

所以,这是一个非常开放的问题,实现它的可能性几乎是无限的……但是,无论如何我都会尝试一下。这个答案不是教条,它只是一种可能的实现方式。

让我们首先希望我们可以随心所欲地放置我们的 table 。要将一个表与另一个表区分开来,我们所要做的就是传递一个 tableId 属性,该属性将 Table 链接到存储中的相应数据。

<Table tableId='customers' />
<Table tableId='products' />
<Table tableId='orders' />
<Table tableId='transactions' />

接下来,让我们定义一个要使用的状态形状。每个顶级键都与我们希望为其存储数据的 tableId 相匹配。你可以把这个形状做成任何你喜欢的形状,但我提供这个是为了让你不必做太多的心理想象,因为我在后面的代码中引用了一些东西。您还可以根据需要命名顶级 tableId 键,只要您一致地引用它们即可。

{
   customers: {
     page: 0,
     sortBy: 'id',
     cols: [
       {id: 'id',         name: 'name'},
       {id: 'username',   name: 'Username'},
       {id: 'first_name', name: 'First Name'},
       {id: 'last_name',  name: 'Last Name'},
       ...
     ],
     rows: [
       {id: 1, username: 'bob', first_name: 'Bob', last_name: 'Smith', ...},
       ...
     ]
  },
  products: {
    ...
  },
  orders: {
    ...
  }
  ...
}

现在,让我们解决最困难的部分:Table 容器。一次要消化的东西很多,但别担心,我会逐一分解重要的部分。

容器/Table.js

import React from 'react';
import {connect} from 'react-redux';
import actions as * from './actions/';

const _Table = ({cols, rows, onClickSort, onClickNextPage}) => (
  <div>
    <table>
      <thead>
        <tr>
         {cols.map((col,key) => (
           <Th key={key} onClickSort={onClickSort(col.id)}>{col.name}</Th>
         )}
        </tr>
      </thead>
      <tbody>
        {rows.map((row,key) => ...}
      </tbody>
    </table>
    <button onClick={onClickNextPage}>Next Page</button>
  </div>
);

const Table = connect(
  ({table}, {tableId}) => ({    // example: if tableId = "customers" ...
    cols: table[tableId].cols,  // state.table.customers.cols
    rows: table[tableId].rows   // state.table.customers.rows
  }),
  (dispatch, {tableId}) => ({
    onClickSort: columnId => event => {
      dispatch(actions.tableSortColumn(tableId, columnId));
      // example: if user clicks 'last_name' column in customers table
      // dispatch(actions.tableSortColumn('customers', 'last_name'));
    },
    onClickNextPage: event => {
      dispatch(actions.tableNextPage(tableId))
    }
  })
)(_Table);

export default Table;

If you only learn one thing from this post, let it be this:

这里要注意的是 mapStateToPropsmapDispatchToProps accepts a second argument称为 ownProps

// did you know you can pass a second arg to these functions ?
const MyContainer = connect({
  (state, ownProps) => ...
  (dispatch, ownProps) => ...
})(...);

在我上面写的容器中,我正在解构我们关心的每个变量。

// Remember how I used the container ?
// here ownProps = {tableId: "customers"}
<Table tableId="customers" />

现在看看我是如何使用connect

const Table = connect(
  // mapStateToProps
  ({table}, {tableId}) => ...
    // table = state.table
    // tableId = ownProps.tableId = "customers"


  // mapDispatchToProps
  (dispatch, {tableId}) => ...
    // dispatch = dispatch
    // tableId = ownProps.tableId = "customers"
)(_Table);

这样,当我们为底层组件 (_Table) 创建调度处理程序时,我们将在处理程序中获得可用的 tableId。如果您不希望 _Table 组件本身甚至不必关心 tableId 属性,这实际上有点不错。

接下来注意 onClickSort 函数的定义方式。

onClickSort: columnId => event => {
  dispatch(actions.tableSortColumn(tableId, columnId));
}

_Table 组件将此函数传递给 Th 使用

<Th key={key} onClickSort={onClickSort(col.id)}>{col.name}</Th>

看看它如何columnId 发送到此处的处理程序?接下来,我们将了解 Th 是如何发送 event 的,它最终会调度 action。

组件/表/Th.js

import React from 'react';

const Th = ({onClickSort, children}) => (
  <th>
    <a href="#sort" onClickSort={event => {
      event.preventDefault();
      onClickSort(event);
    }}>{children}</a>
  </th>
);

export default Th;

如果您不需要 event,则没有必要保留它,但我想我会向您展示如何附加它,以防您想将其用于某些用途。

继续...

actions/index.js

您的操作文件将看起来非常标准。请注意,我们在每个表操作中提供了对 tableId 的访问。

export const TABLE_SORT_COLUMN = 'TABLE_SORT_COLUMN';
export const TABLE_NEXT_PAGE = 'TABLE_NEXT_PAGE';

export const tableSortColumn = (tableId, columnId) => ({
  type: TABLE_SORT_COLUMN, payload: {tableId, columnId}
});

export const tableNextPage = (tableId) => ({
  type: TABLE_NEXT_PAGE, payload: {tableId}
});

...

最后,您的 tableReducer 可能看起来像这样。同样,这里没有什么特别的。您将对操作类型执行常规 切换,然后相应地更新状态。您可以使用 action.payload.tableId 影响状态的适当部分。请记住我提出的状态形状。如果您选择不同的状态形状,则必须更改此代码以匹配

const defaultState = {
  customers: {
    page: 0,
    sortBy: null,
    cols: [],
    rows: []
  }
};

// deep object assignment for nested objects
const tableAssign = (state, tableId, data) =>
  Object.assign({}, state, {
    [tableId]: Object.assign({}, state[tableId], data)
  });

const tableReducer = (state=defaultState, {type, payload}) => {
  switch (type) {
    case TABLE_SORT_COLUMN:
      return tableAssign(state, payload.tableId, {
        sortBy: payload.columnId
      });
    case TABLE_NEXT_PAGE:
      return tableAssign(state, payload.tableId, {
        page: state[payload.tableId].page + 1
      });
    default:
      return state;
  }
};

备注:

我不打算讨论表数据的异步加载。这样的工作流程已经在 redux 文档中得到了很好的详细说明:Async Actions .您可以选择 redux-thunk、redux-promise 或 redux-saga。选择你最了解的!正确实现 TABLE_DATA_FETCH 的关键是确保像我在其他 onClick* 中那样发送 tableId(以及您需要的任何其他参数) > 处理程序。

关于javascript - react redux 架构表操作和缩减器,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/35841558/

有关javascript - react redux 架构表操作和缩减器的更多相关文章

  1. ruby - 如何使用 Selenium Webdriver 根据 div 的内容执行操作? - 2

    我有一个使用SeleniumWebdriver和Nokogiri的Ruby应用程序。我想选择一个类,然后对于那个类对应的每个div,我想根据div的内容执行一个Action。例如,我正在解析以下页面:https://www.google.com/webhp?sourceid=chrome-instant&ion=1&espv=2&ie=UTF-8#q=puppies这是一个搜索结果页面,我正在寻找描述中包含“Adoption”一词的第一个结果。因此机器人应该寻找带有className:"result"的div,对于每个检查它的.descriptiondiv是否包含单词“adoption

  2. ruby-on-rails - 如何处理 Grape 中特定操作的过滤器之前? - 2

    我正在我的Rails项目中安装Grape以构建RESTfulAPI。现在一些端点的操作需要身份验证,而另一些则不需要身份验证。例如,我有users端点,看起来像这样:moduleBackendmoduleV1classUsers现在如您所见,除了password/forget之外的所有操作都需要用户登录/验证。创建一个新的端点也没有意义,比如passwords并且只是删除password/forget从逻辑上讲,这个端点应该与用户资源。问题是Grapebefore过滤器没有像except,only这样的选项,我可以在其中说对某些操作应用过滤器。您通常如何干净利落地处理这种情况?

  3. ruby-on-rails - 在 Ruby on Rails 中发送响应之前如何等待多个异步操作完成? - 2

    在我做的一些网络开发中,我有多个操作开始,比如对外部API的GET请求,我希望它们同时开始,因为一个不依赖另一个的结果。我希望事情能够在后台运行。我找到了concurrent-rubylibrary这似乎运作良好。通过将其混合到您创建的类中,该类的方法具有在后台线程上运行的异步版本。这导致我编写如下代码,其中FirstAsyncWorker和SecondAsyncWorker是我编写的类,我在其中混合了Concurrent::Async模块,并编写了一个名为“work”的方法来发送HTTP请求:defindexop1_result=FirstAsyncWorker.new.async.

  4. ruby-on-rails - 使用 javascript 更改数据方法不会更改 ajax 调用用户的什么方法? - 2

    我遇到了一个非常奇怪的问题,我很难解决。在我看来,我有一个与data-remote="true"和data-method="delete"的链接。当我单击该链接时,我可以看到对我的Rails服务器的DELETE请求。返回的JS代码会更改此链接的属性,其中包括href和data-method。再次单击此链接后,我的服务器收到了对新href的请求,但使用的是旧的data-method,即使我已将其从DELETE到POST(它仍然发送一个DELETE请求)。但是,如果我刷新页面,HTML与"new"HTML相同(随返回的JS发生变化),但它实际上发送了正确的请求类型。这就是这个问题令我困惑的

  5. ruby - 在 Ruby 中是否有一种惯用的方法来操作 2 个数组? - 2

    a=[3,4,7,8,3]b=[5,3,6,8,3]假设数组长度相同,是否有办法使用each或其他一些惯用方法从两个数组的每个元素中获取结果?不使用计数器?例如获取每个元素的乘积:[15,12,42,64,9](0..a.count-1).eachdo|i|太丑了...ruby1.9.3 最佳答案 使用Array.zip怎么样?:>>a=[3,4,7,8,3]=>[3,4,7,8,3]>>b=[5,3,6,8,3]=>[5,3,6,8,3]>>c=[]=>[]>>a.zip(b)do|i,j|c[[3,5],[4,3],[7,6],

  6. ruby-on-rails - 如何让 Rails View 返回其关联的操作名称? - 2

    我有一个非常简单的Controller来管理我的Rails应用程序中的静态页面:classPagesController我怎样才能让View模板返回它自己的名字,这样我就可以做这样的事情:#pricing.html.erb#-->"Pricing"感谢您的帮助。 最佳答案 4.3RoutingParametersTheparamshashwillalwayscontainthe:controllerand:actionkeys,butyoushouldusethemethodscontroller_nameandaction_nam

  7. ruby - Ruby 和 Ruby on Rails 中的三层架构 - 2

    我是一名决定学习Ruby和RubyonRails的ASP.NETMVC开发人员。我已经有所了解并在RoR上创建了一个网站。在ASP.NETMVC上开发,我一直使用三层架构:数据层、业务层和UI(或表示)层。尝试在RubyonRails应用程序中使用这种方法,我发现没有关于它的信息(或者也许我只是找不到它?)。也许有人可以建议我如何在RubyonRails上创建或使用三层架构?附言我使用ruby​​1.9.3和RubyonRails3.2.3。 最佳答案 我建议在制作RoR应用程序时遵循RubyonRails(RoR)风格。Rails

  8. ruby - 在 Mechanize 中使用 JavaScript 单击链接 - 2

    我有这个:AccountSummary我想单击该链接,但在使用link_to时出现错误。我试过:bot.click(page.link_with(:href=>/menu_home/))bot.click(page.link_with(:class=>'top_level_active'))bot.click(page.link_with(:href=>/AccountSummary/))我得到的错误是:NoMethodError:nil:NilClass的未定义方法“[]” 最佳答案 那是一个javascript链接。Mechan

  9. Postman测试简单操作 - 2

    1、接口请求基本操作1.1例子tips在view的选项可以zoomin调整窗口字帖大小。1、创建一个测试的workspace,并命名为test2、test后面新增一个addrequest3、选择发送GET,URL为一个开源的https://api.apiopen.top/api/sentences获取每日一句4、点击send查看内容Tips:如果提示出现Error:tunnelingsocketcouldnotbeestablished,statusCode=407错误,参照以下解决办法)关于tunnelingsocketcouldnotbeestablished,cause=getaddri

  10. 【Linux操作系统】——网络配置与SSH远程 - 2

    Linux操作系统——网络配置与SSH远程安装完VMware与系统后,需要进行网络配置。第一个目标为进行SSH连接,可以从本机到VMware进行文件传送,首先需要进行网络配置。1.下载远程软件首先需要先下载安装一款远程软件:FinalShell或者xhell7FinalShellxhell7FinalShell下载:Windows下载http://www.hostbuf.com/downloads/finalshell_install.exemacOS下载http://www.hostbuf.com/downloads/finalshell_install.pkg2.配置CentOS网络安装好

随机推荐