笔记本键盘 发疯_如何使用ImmutableJS而不会发疯

2024-01-12 08:20

本文主要是介绍笔记本键盘 发疯_如何使用ImmutableJS而不会发疯,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

笔记本键盘 发疯

如果您在Javascript生态系统中,则可能会知道有很多新库,并且仍在开发良好实践,尤其是在ReactJS生态系统中。

如果您正在使用ReactJS开发单页应用程序,并且正在使用Redux进行状态管理 ,那么您可能听说过ImmutableJS。

它是一个库,可帮助开发人员保持其状态不变,从而避免难以发现的错误。

不仅如此,它还有助于React在适当的时间重新渲染组件。 您可能知道React使用浅层比较来了解某些道具是否已更改。

当您将结构化数据传递到组件(数组,对象)时,这可能会带来问题。 React不会执行深度比较(因为它很昂贵),因此您可能会遇到意想不到的行为。

// In reducer.js
const state = {
usersPage: {
loading: false,
isFetched: false,
list: [],
},
companiesPage: {
loading: false,
isFetched: false,
list: [],
},
};
// In UsersPage.js
class UsersPage extends React.Component {
render() {
const { loading, list } = this.props.usersPage;
    return (
<div>
{ loading && <LoadingIndicator /> }
{ !loading && <UsersTable list={list} /> }
</div>
);
}
}
function mapStateToProps(state) {
return {
usersPage: selectors.selectUsersPage(state) ,
};
}
// In selectors.js
const selectUsersPage = (state) => state.usersPage;

考虑上面的代码,一切似乎都很好。 但是,如果您以错误的方式实施状态更新,例如 您不会创建新对象,React不会更新您的组件。 例如:

// Incorrect implementation
function reducer(state, action) {
switch(action.type) {
case 'USERS_LOADED':
state.usersPage.list = action.payload;
state.usersPage.loading = false;
state.usersPage.isFetched = true;
return state;
default:
return state;
}
}
// Correct implementation
function reducer(state, action) {
switch(action.type) {
case 'USERS_LOADED':
return {
...state,
usersPage: Object.assign({}, state.usersPage, {
list: Object.payload,
loading: false,
isFetched: true,
}),
};
default:
return state;
}
}

正如您在上面的代码中看到的那样,事情很容易变得棘手。 您必须确保在保留状态的其他分支并正确更新当前分支中的变量的同时返回新对象。

现在,让我们看一下ImmutableJS的实现:

import { fromJS } from 'immutable';
const state = fromJS({
usersPage: {
loading: false,
isFetched: false,
list: [],
},
companiesPage: {
loading: false,
isFetched: false,
list: [],
},
});
function reducer(state, action) {
switch(action.type) {
case 'USERS_LOADED':
return state
.setIn(['usersPage', 'list'], fromJS(action.payload))
.setIn(['usersPage', 'isFetched'], true)
.setIn(['usersPage', 'loading'], false)
;
default:
return state;
}
}

这种语法使事情变得更易于阅读和维护。 每当更改状态时,ImmutableJS都会返回一个新对象。

但是,您将必须使用ImmutableJS getter来获取值:

// In UsersPage.js
class UsersPage extends React.Component {
render() {
const { usersPage } = this.props;
const loading = usersPage.get('loading');
const list = usersPage.get('list').toJS();
    return (
<div>
{ loading && <LoadingIndicator /> }
{ !loading && <UsersTable list={list} /> }
</div>
);
}
}
function mapStateToProps(state) {
return {
usersPage: selectors.selectUsersPage(state),
};
}
// In selectors.js
const selectUsersPage = (state) => state.get('usersPage');

更糟糕的是,有时ImmutableJS对象会更深地泄漏到您的组件中,甚至泄漏到您的呈现组件中(请toJS() )。 这通常是人们反对使用ImmutableJS的原因之一。

当您计划发布软件包时,情况甚至更糟。 您不想让开发人员使用某些库来束缚使用它的开发人员的双手。

那么如何结合两全其美呢?

重新选择救援!

Reselect是一个很棒的库,可以帮助您解决此问题。

让我们首先了解Reselect的用途:

用于Redux的简单“选择器”库。

  • 选择器可以计算派生数据,从而允许Redux存储最小可能状态。
  • 选择器是有效的。 除非更改了其中一个参数,否则不会重新计算选择器。
  • 选择器是可组合的。 它们可以用作其他选择器的输入。

和我裸一下。 考虑以下代码:

import { createSelector } from 'reselect';
const selectUsersPageDomain = (state) => state.get('usersPage');
const makeSelectUsersPage = createSelector(
selectUsersPageDomain,
(substate) => substate.toJS(),
);

然后,您可以再次使用纯JS语法:

import { createStructuredSelector } from 'reselect';
class UsersPage extends React.Component {
render() {
const { loading, list } = this.props.usersPage;
return (
<div>
{ loading && <LoadingIndicator /> }
{ !loading && <UsersTable list={list} /> }
</div>
);
}
}
const mapStateToProps = createStructuredSelector ({
usersPage: selectors.makeSelectUsersPage(),
});

如果userPage分支中没有任何变化,则选择器不会重新计算其值,React 也不会触发组件渲染。

如您所见,使用此策略,您可以将两个方面的优点结合起来。

但是有一件重要的事情,我没有谈论过,并没有明显的好处:

使用ImmutableJS更新某些分支时,所有未触及的分支均保持不变:

const { fromJS } = require('immutable');
const state = fromJS({
usersPage: {
loading: false,
isFetched: false,
list: [],
},
companiesPage: {
loading: false,
isFetched: false,
list: [],
},
});
console.log('Updating usersPage branch');
const state1 = state.setIn(['usersPage', 'loading'], true);
state.get('usersPage') === state1.get('usersPage'); // false
state.get('companiesPage') === state1.get('companiesPage'); // true
state === state1; // false
console.log('Updating companiesPage branch');
const state2 = state1.setIn(['companiesPage', 'loading'], true);
state1.get('usersPage') === state2.get('usersPage'); // true
state1.get('companiesPage') === state2.get('companiesPage'); // false
state1 === state2; // false

注意:我删除了 console.log 语句以缩短行数。

如果您查看代码,将会看到未更新的分支保持不变->因此, reselect选择选择器将不会触发更新。 仅更新分支的选择器将被更新

但是状态的根也被更新了! 因此,在编写选择器时必须小心一点:

// In reducer.js
const { fromJS } = require('immutable');
const state = fromJS({
pages: { // We put our pages 1 more level deeper
usersPage: {
loading: false,
isFetched: false,
list: [],
},
companiesPage: {
loading: false,
isFetched: false,
list: [],
},
}
});
// In selectors.js
// Incorrect implementation
import { createSelector } from 'reselect';
const selectPagesDomain = (state) => state.get('pages');
const selectUsersPageDomain = (state) => state.get('usersPage');
const selectCompaniesPageDomain = (state) => state.get('companiesPage');
const makeSelectUsersPage = createSelector(
selectPagesDomain , // source of problem
selectUsersPageDomain,
(substate) => substate.toJS(),
);
const makeSelectCompaniesPage = createSelector(
selectPagesDomain , // source of problem
selectCompaniesPageDomain,
(substate) => substate.toJS(),
);

如果您以这种方式实现选择器,则仅在其中之一中更改值时,都会同时触发makeSelectCompaniesPagemakeSelectUsersPage 。 这可能是您不想要的东西。

他们为什么被触发? 回顾以下规则:

  • 选择器是有效的。 除非更改了其中一个参数,否则不会重新计算选择器。

由于selectPagesDomain如果分支的任何一个已更改,将返回不同的值,因此reselect每次都会重新计算两个选择器。

这是正确的实现:

// Correct implementation
import { createSelector } from 'reselect';
const selectUsersPageDomain = (state) => state.getIn(['pages', 'usersPage']) ;
const selectCompaniesPageDomain = (state) => state.getIn(['pages', 'companiesPage']) ;
const makeSelectUsersPage = createSelector(
selectUsersPageDomain ,
(substate) => substate.toJS(),
);
const makeSelectCompaniesPage = createSelector(
selectCompaniesPageDomain ,
(substate) => substate.toJS(),
);

您希望选择最接近要在选择器中使用的数据的路径,以防止在更改其他分支时重新计算。

如果您成功到这里,恭喜!

让我们放在一起:

 // In reducer.js
const { fromJS } = require('immutable');
const state = fromJS({
pages: {
usersPage: {
loading: false,
isFetched: false,
list: [],
},
companiesPage: {
loading: false,
isFetched: false,
list: [],
},
}
});
function reducer(state, action) {
switch(action.type) {
case 'USERS_LOADED':
return state
.setIn(['usersPage', 'list'], fromJS(action.payload))
.setIn(['usersPage', 'isFetched'], true)
.setIn(['usersPage', 'loading'], false)
;
case 'COMPANIES_LOADED':
return state
.setIn(['companiesPage', 'list'], fromJS(action.payload))
.setIn(['companiesPage', 'isFetched'], true)
.setIn(['companiesPage', 'loading'], false)
;
default:
return state;
}
}
export default reducer;
 // In selectors.js
import { createSelector } from 'reselect';
const selectUsersPageDomain = (state) => state.getIn(['pages', 'usersPage']);
const selectCompaniesPageDomain = (state) => state.getIn(['pages', 'companiesPage']);
export const makeSelectUsersPage = createSelector(
selectUsersPageDomain,
(substate) => substate.toJS(),
);
export const makeSelectCompaniesPage = createSelector(
selectCompaniesPageDomain,
(substate) => substate.toJS(),
);
 // In UsersPage.js
import { createStructuredSelector } from 'reselect';
import * as selectors from './selectors';
class UsersPage extends React.Component {
render() {
const { loading, list } = this.props.usersPage;
    return (
<div>
{ loading && <LoadingIndicator /> }
{ !loading && <UsersTable list={list} /> }
</div>
);
}
}
const mapStateToProps = createStructuredSelector({
usersPage: selectors.makeSelectUsersPage(),
});
export default connect(mapStateToProps)(UsersPage);
 // In CompaniesPage.js
import { createStructuredSelector } from 'reselect';
import * as selectors from './selectors';
class CompaniesPage extends React.Component {
render() {
const { loading, list } = this.props.companiesPage;
    return (
<div>
{ loading && <LoadingIndicator /> }
{ !loading && <CompaniesTable list={list} /> }
</div>
);
}
}
const mapStateToProps = createStructuredSelector({
companiesPage: selectors.makeSelectCompaniesPage(),
});
export default connect(mapStateToProps)(CompaniesPage);

而已!

现在,您可以使用ImmutableJS的所有优点,而无需在容器或呈现组件中使用其getter。

最重要的是,您获得了重新选择库的优势,这可以帮助您提高选择器的效率。

让我知道你的想法!

参考文献:

Redux
如果您不使用模块捆绑器,也可以。 npm软件包包括预编译的生产和开发… redux.js.org
reactjs /重新选择
重新选择 -Redux github.com的选择器库

翻译自: https://hackernoon.com/how-to-use-immutablejs-without-going-crazy-bfcb805a0043

笔记本键盘 发疯

这篇关于笔记本键盘 发疯_如何使用ImmutableJS而不会发疯的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/597289

相关文章

Python 函数详解:从基础语法到高级使用技巧

《Python函数详解:从基础语法到高级使用技巧》本文基于实例代码,全面讲解Python函数的定义、参数传递、变量作用域及类型标注等知识点,帮助初学者快速掌握函数的使用技巧,感兴趣的朋友跟随小编一起... 目录一、函数的基本概念与作用二、函数的定义与调用1. 无参函数2. 带参函数3. 带返回值的函数4.

MySQL中DATE_FORMAT时间函数的使用小结

《MySQL中DATE_FORMAT时间函数的使用小结》本文主要介绍了MySQL中DATE_FORMAT时间函数的使用小结,用于格式化日期/时间字段,可提取年月、统计月份数据、精确到天,对大家的学习或... 目录前言DATE_FORMAT时间函数总结前言mysql可以使用DATE_FORMAT获取日期字段

Go语言使用sync.Mutex实现资源加锁

《Go语言使用sync.Mutex实现资源加锁》数据共享是一把双刃剑,Go语言为我们提供了sync.Mutex,一种最基础也是最常用的加锁方式,用于保证在任意时刻只有一个goroutine能访问共享... 目录一、什么是 Mutex二、为什么需要加锁三、实战案例:并发安全的计数器1. 未加锁示例(存在竞态)

setsid 命令工作原理和使用案例介绍

《setsid命令工作原理和使用案例介绍》setsid命令在Linux中创建独立会话,使进程脱离终端运行,适用于守护进程和后台任务,通过重定向输出和确保权限,可有效管理长时间运行的进程,本文给大家介... 目录setsid 命令介绍和使用案例基本介绍基本语法主要特点命令参数使用案例1. 在后台运行命令2.

使用Redis快速实现共享Session登录的详细步骤

《使用Redis快速实现共享Session登录的详细步骤》在Web开发中,Session通常用于存储用户的会话信息,允许用户在多个页面之间保持登录状态,Redis是一个开源的高性能键值数据库,广泛用于... 目录前言实现原理:步骤:使用Redis实现共享Session登录1. 引入Redis依赖2. 配置R

使用Python的requests库调用API接口的详细步骤

《使用Python的requests库调用API接口的详细步骤》使用Python的requests库调用API接口是开发中最常用的方式之一,它简化了HTTP请求的处理流程,以下是详细步骤和实战示例,涵... 目录一、准备工作:安装 requests 库二、基本调用流程(以 RESTful API 为例)1.

使用Python开发一个Ditto剪贴板数据导出工具

《使用Python开发一个Ditto剪贴板数据导出工具》在日常工作中,我们经常需要处理大量的剪贴板数据,下面将介绍如何使用Python的wxPython库开发一个图形化工具,实现从Ditto数据库中读... 目录前言运行结果项目需求分析技术选型核心功能实现1. Ditto数据库结构分析2. 数据库自动定位3

Python yield与yield from的简单使用方式

《Pythonyield与yieldfrom的简单使用方式》生成器通过yield定义,可在处理I/O时暂停执行并返回部分结果,待其他任务完成后继续,yieldfrom用于将一个生成器的值传递给另一... 目录python yield与yield from的使用代码结构总结Python yield与yield

Go语言使用select监听多个channel的示例详解

《Go语言使用select监听多个channel的示例详解》本文将聚焦Go并发中的一个强力工具,select,这篇文章将通过实际案例学习如何优雅地监听多个Channel,实现多任务处理、超时控制和非阻... 目录一、前言:为什么要使用select二、实战目标三、案例代码:监听两个任务结果和超时四、运行示例五

python使用Akshare与Streamlit实现股票估值分析教程(图文代码)

《python使用Akshare与Streamlit实现股票估值分析教程(图文代码)》入职测试中的一道题,要求:从Akshare下载某一个股票近十年的财务报表包括,资产负债表,利润表,现金流量表,保存... 目录一、前言二、核心知识点梳理1、Akshare数据获取2、Pandas数据处理3、Matplotl