笔记本键盘 发疯_如何使用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使用FastAPI实现大文件分片上传与断点续传功能

《Python使用FastAPI实现大文件分片上传与断点续传功能》大文件直传常遇到超时、网络抖动失败、失败后只能重传的问题,分片上传+断点续传可以把大文件拆成若干小块逐个上传,并在中断后从已完成分片继... 目录一、接口设计二、服务端实现(FastAPI)2.1 运行环境2.2 目录结构建议2.3 serv

Spring Security简介、使用与最佳实践

《SpringSecurity简介、使用与最佳实践》SpringSecurity是一个能够为基于Spring的企业应用系统提供声明式的安全访问控制解决方案的安全框架,本文给大家介绍SpringSec... 目录一、如何理解 Spring Security?—— 核心思想二、如何在 Java 项目中使用?——

springboot中使用okhttp3的小结

《springboot中使用okhttp3的小结》OkHttp3是一个JavaHTTP客户端,可以处理各种请求类型,比如GET、POST、PUT等,并且支持高效的HTTP连接池、请求和响应缓存、以及异... 在 Spring Boot 项目中使用 OkHttp3 进行 HTTP 请求是一个高效且流行的方式。

Java使用Javassist动态生成HelloWorld类

《Java使用Javassist动态生成HelloWorld类》Javassist是一个非常强大的字节码操作和定义库,它允许开发者在运行时创建新的类或者修改现有的类,本文将简单介绍如何使用Javass... 目录1. Javassist简介2. 环境准备3. 动态生成HelloWorld类3.1 创建CtC

使用Python批量将.ncm格式的音频文件转换为.mp3格式的实战详解

《使用Python批量将.ncm格式的音频文件转换为.mp3格式的实战详解》本文详细介绍了如何使用Python通过ncmdump工具批量将.ncm音频转换为.mp3的步骤,包括安装、配置ffmpeg环... 目录1. 前言2. 安装 ncmdump3. 实现 .ncm 转 .mp34. 执行过程5. 执行结

Java使用jar命令配置服务器端口的完整指南

《Java使用jar命令配置服务器端口的完整指南》本文将详细介绍如何使用java-jar命令启动应用,并重点讲解如何配置服务器端口,同时提供一个实用的Web工具来简化这一过程,希望对大家有所帮助... 目录1. Java Jar文件简介1.1 什么是Jar文件1.2 创建可执行Jar文件2. 使用java

C#使用Spire.Doc for .NET实现HTML转Word的高效方案

《C#使用Spire.Docfor.NET实现HTML转Word的高效方案》在Web开发中,HTML内容的生成与处理是高频需求,然而,当用户需要将HTML页面或动态生成的HTML字符串转换为Wor... 目录引言一、html转Word的典型场景与挑战二、用 Spire.Doc 实现 HTML 转 Word1

Java中的抽象类与abstract 关键字使用详解

《Java中的抽象类与abstract关键字使用详解》:本文主要介绍Java中的抽象类与abstract关键字使用详解,本文通过实例代码给大家介绍的非常详细,感兴趣的朋友跟随小编一起看看吧... 目录一、抽象类的概念二、使用 abstract2.1 修饰类 => 抽象类2.2 修饰方法 => 抽象方法,没有

MyBatis ParameterHandler的具体使用

《MyBatisParameterHandler的具体使用》本文主要介绍了MyBatisParameterHandler的具体使用,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参... 目录一、概述二、源码1 关键属性2.setParameters3.TypeHandler1.TypeHa

Spring 中的切面与事务结合使用完整示例

《Spring中的切面与事务结合使用完整示例》本文给大家介绍Spring中的切面与事务结合使用完整示例,本文通过实例代码给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友参考... 目录 一、前置知识:Spring AOP 与 事务的关系 事务本质上就是一个“切面”二、核心组件三、完