Unstated Next readme 的中文翻译

前言

这个库的作者希望使用 React 内置 API ,直接实现状态管理的功能。看完这个库的说明后,没有想到代码可以这个玩。短短几行代码,仅仅使用 React Hooks ,就实现了状态管理的功能。

看完之后,第一想法就是翻译成中文,分享给其他人。提交 Pull Request 后,库作者将我的翻译合并了。同时作者欢迎将 README 翻译成其他语言,以下是全部翻译内容,不妥之处欢迎指正或 Pull Request.

Unstated Next

永远不必再考虑 React 状态管理了,仅仅 200 字节的状态管理解决方案。

  • React Hooks React Hooks 用做你所有的状态管理。
  • ~200 bytes min+gz.
  • 熟悉的 API 仅仅使用了 React,没有依赖第三方库。
  • 最小 API 只需 5 分钟学习。
  • TypeScript 编写 推断代码更容易,易于编写 React 代码。

但是,最重要的问题是:这比 Redux 更好吗? 答案可能是。

  • 它更小。 比 Redux 小 40 倍。
  • 它更快。 组件性能问题。
  • 它更容易学习。 你必须已经知道 React Hooks 和 Context 。只需使用它们,它们就会嗨起来。
  • 更容易集成。 一次集成一个组件,并且轻松与其他 React 库集成。
  • 它更容易测试。 测试 reducers 纯属浪费你的时间,这个库使你更容易测试 React 组件。
  • 它更容易进行类型检查。 旨在使你的大多数类型可推断。
  • 它是最小的。 仅仅使用了 React 。

你自己看着办吧!

查看 Unstated 迁移手册 →

安装

npm install --save unstated-next

Example

import React, { useState } from "react"
import { createContainer } from "unstated-next"
import { render } from "react-dom" function useCounter() {
let [count, setCount] = useState(0)
let decrement = () => setCount(count - 1)
let increment = () => setCount(count + 1)
return { count, decrement, increment }
} let Counter = createContainer(useCounter) function CounterDisplay() {
let counter = Counter.useContainer()
return (
<div>
<button onClick={counter.decrement}>-</button>
<span>{counter.count}</span>
<button onClick={counter.increment}>+</button>
</div>
)
} function App() {
return (
<Counter.Provider>
<CounterDisplay />
<CounterDisplay />
</Counter.Provider>
)
} render(<App />, document.getElementById("root"))

API

createContainer(useHook)

import { createContainer } from "unstated-next"

function useCustomHook() {
let [value, setInput] = useState()
let onChange = e => setValue(e.currentTarget.value)
return { value, onChange }
} let Container = createContainer(useCustomHook)
// Container === { Provider, useContainer }

<Container.Provider>

function ParentComponent() {
return (
<Container.Provider>
<ChildComponent />
</Container.Provider>
)
}

Container.useContainer()

function ChildComponent() {
let input = Container.useContainer()
return <input value={input.value} onChange={input.onChange} />
}

useContainer(Container)

import { useContainer } from "unstated-next"

function ChildComponent() {
let input = useContainer(Container)
return <input value={input.value} onChange={input.onChange} />
}

指南

如果你以前从未使用过 React Hooks,我不建议你往下看,请先阅读 [React 官网的 React Hooks 文档](https://reactjs.org/docs/hooks-intro.html)。

首先,使用 React Hooks,你可以创建这样一个组件:

function CounterDisplay() {
let [count, setCount] = useState(0)
let decrement = () => setCount(count - 1)
let increment = () => setCount(count + 1)
return (
<div>
<button onClick={decrement}>-</button>
<p>You clicked {count} times</p>
<button onClick={increment}>+</button>
</div>
)
}

然后,如果你想共享组件的逻辑,你可以把它写在组件外面,自定义一个 hook:

function useCounter() {
let [count, setCount] = useState(0)
let decrement = () => setCount(count - 1)
let increment = () => setCount(count + 1)
return { count, decrement, increment }
} function CounterDisplay() {
let counter = useCounter()
return (
<div>
<button onClick={counter.decrement}>-</button>
<p>You clicked {counter.count} times</p>
<button onClick={counter.increment}>+</button>
</div>
)
}

但是,除了共享逻辑之外,你还想共享状态,你会怎么做呢?

这个时候,context 就发挥了作用:

function useCounter() {
let [count, setCount] = useState(0)
let decrement = () => setCount(count - 1)
let increment = () => setCount(count + 1)
return { count, decrement, increment }
} let Counter = createContext(null) function CounterDisplay() {
let counter = useContext(Counter)
return (
<div>
<button onClick={counter.decrement}>-</button>
<p>You clicked {counter.count} times</p>
<button onClick={counter.increment}>+</button>
</div>
)
} function App() {
let counter = useCounter()
return (
<Counter.Provider value={counter}>
<CounterDisplay />
<CounterDisplay />
</Counter.Provider>
)
}

这很棒,也很完美,更多人应该编写这样的代码。

但有时我们需要更多的结构和特定的 API 设计才能使其始终保持正确。

通过引入 createContainer() 函数,你可以将自定义 hooks 作为 containers,并且定义明确的 API,防止错误使用。

import { createContainer } from "unstated-next"

function useCounter() {
let [count, setCount] = useState(0)
let decrement = () => setCount(count - 1)
let increment = () => setCount(count + 1)
return { count, decrement, increment }
} let Counter = createContainer(useCounter) function CounterDisplay() {
let counter = Counter.useContainer()
return (
<div>
<button onClick={counter.decrement}>-</button>
<p>You clicked {counter.count} times</p>
<button onClick={counter.increment}>+</button>
</div>
)
} function App() {
return (
<Counter.Provider>
<CounterDisplay />
<CounterDisplay />
</Counter.Provider>
)
}

下面是前后的代码对比:

- import { createContext, useContext } from "react"
+ import { createContainer } from "unstated-next" function useCounter() {
...
} - let Counter = createContext(null)
+ let Counter = createContainer(useCounter) function CounterDisplay() {
- let counter = useContext(Counter)
+ let counter = Counter.useContainer()
return (
<div>
...
</div>
)
} function App() {
- let counter = useCounter()
return (
- <Counter.Provider value={counter}>
+ <Counter.Provider>
<CounterDisplay />
<CounterDisplay />
</Counter.Provider>
)
}

如果你正在使用 TypeScript(我鼓励你了解更多关于它的信息),这也有助于 TypeScript 的内置推断做得更好。只要你的自定义 hook 类型是完善的,那么类型都会自动推断。

提示

提示 #1: 组合 Containers

因为我们只使用了自定义 React hooks,所以可以在其他 hooks 内部组合 containers。

function useCounter() {
let [count, setCount] = useState(0)
let decrement = () => setCount(count - 1)
let increment = () => setCount(count + 1)
return { count, decrement, increment, setCount }
} let Counter = createContainer(useCounter) function useResettableCounter() {
let counter = Counter.useContainer()
let reset = () => counter.setCount(0)
return { ...counter, reset }
}

提示 #2: 保持 Containers 很小

这对于保持 containers 小而集中非常有用。 如果你想在 containers 中对代码进行逻辑拆分,那么这一点非常重要。只需将它们移动到自己的 hooks 中,仅保存 containers 的状态即可。

function useCount() {
return useState(0)
} let Count = createContainer(useCount) function useCounter() {
let [count, setCount] = Count.useContainer()
let decrement = () => setCount(count - 1)
let increment = () => setCount(count + 1)
let reset = () => setCount(0)
return { count, decrement, increment, reset }
}

提示 #3: 优化组件

unstated-next 无需优化。所有你要做的优化,都是标准的 React 优化。

1) 通过拆分组件来优化耗时的子树

优化前:

function CounterDisplay() {
let counter = Counter.useContainer()
return (
<div>
<button onClick={counter.decrement}>-</button>
<p>You clicked {counter.count} times</p>
<button onClick={counter.increment}>+</button>
<div>
<div>
<div>
<div>SUPER EXPENSIVE RENDERING STUFF</div>
</div>
</div>
</div>
</div>
)
}

优化后:

function ExpensiveComponent() {
return (
<div>
<div>
<div>
<div>SUPER EXPENSIVE RENDERING STUFF</div>
</div>
</div>
</div>
)
} function CounterDisplay() {
let counter = Counter.useContainer()
return (
<div>
<button onClick={counter.decrement}>-</button>
<p>You clicked {counter.count} times</p>
<button onClick={counter.increment}>+</button>
<ExpensiveComponent />
</div>
)
}

2) 使用 useMemo() 优化耗时的操作

优化前:

function CounterDisplay(props) {
let counter = Counter.useContainer() // 每次 `counter` 改变都要重新计算这个值,非常耗时
let expensiveValue = expensiveComputation(props.input) return (
<div>
<button onClick={counter.decrement}>-</button>
<p>You clicked {counter.count} times</p>
<button onClick={counter.increment}>+</button>
</div>
)
}

优化后:

function CounterDisplay(props) {
let counter = Counter.useContainer() // 仅在输入更改时重新计算这个值
let expensiveValue = useMemo(() => {
return expensiveComputation(props.input)
}, [props.input]) return (
<div>
<button onClick={counter.decrement}>-</button>
<p>You clicked {counter.count} times</p>
<button onClick={counter.increment}>+</button>
</div>
)
}

3) 使用 React.memo()、useCallback() 减少重新渲染次数

优化前:

function useCounter() {
let [count, setCount] = useState(0)
let decrement = () => setCount(count - 1)
let increment = () => setCount(count + 1)
return { count, decrement, increment }
} let Counter = createContainer(useCounter) function CounterDisplay(props) {
let counter = Counter.useContainer()
return (
<div>
<button onClick={counter.decrement}>-</button>
<p>You clicked {counter.count} times</p>
<button onClick={counter.increment}>+</button>
</div>
)
}

优化后:

function useCounter() {
let [count, setCount] = useState(0)
let decrement = useCallback(() => setCount(count - 1), [count])
let increment = useCallback(() => setCount(count + 1), [count])
return { count, decrement, increment }
} let Counter = createContainer(useCounter) let CounterDisplayInner = React.memo(props => {
return (
<div>
<button onClick={props.decrement}>-</button>
<p>You clicked {props.count} times</p>
<button onClick={props.increment}>+</button>
</div>
)
}) function CounterDisplay(props) {
let counter = Counter.useContainer()
return <CounterDisplayInner {...counter} />
}

与 Unstated 的关系

我认为这个库是 Unstated 精神的继承者。因为我相信 React 在状态管理方面已经非常出色,唯一缺少的就是轻松共享状态和逻辑,所以我创建了 Unstated 。我创建的 Unstated 是 React 共享状态和逻辑的 最小 解决方案。

然而,使用 Hooks,React 在共享状态和逻辑方面可以做得更好。我甚至认为 Unstated 成为了不必要的抽象。

但是,我认为很多开发人员都在努力了解如何使用 React Hooks 共享状态和逻辑,从而实现应用程序共享状态。这可能只是文档和社区动力的问题,但我认为一个新的 API 可以弥补这种心理差距。

这个 API 就是 Unstated Next。 它不是 React 中共享状态和逻辑的最小 API,而是用于理解如何在 React 中共享状态和逻辑的最小 API

我一直给 React 站队。我希望 React 可以赢。 我希望社区放弃像 Redux 这样的状态管理库,并找到使用 React 内置工具链的更好方法。

如果你不想使用 Unstated,你只想使用 React 本身,我非常鼓励你这么做。 写关于它的博客文章! 讨论它! 在社区中传播你的知识。

unstated 迁移

我故意将其发布为单独的包,因为它是对原有 API 的完全重写。 这样,你可以逐步安装和迁移。

请向我提供有关该迁移过程的反馈,因为在接下来的几个月里,我希望得到这些反馈并做以下两件事:

  • 确保 unstated-next 满足 unstated 使用者的所有需求。
  • 确保 unstated 使用者的代码可以完整地迁移到 unstated-next

我可以将 API 新增到两者的任意一个仓库中,从而使开发人员工作得更轻松。 对于 unstated-next,我将保证新增的 API 尽可能小,同时,我也会尽量保持库很小。

未来,我可能会将 unstated-next 合并为 unstated 的主要版本。 unstated-next 仍然存在,这样你就可以安装 unstated@2unstated-next。 当你完成迁移后,你可以更新到 unstated@3 ,同时删除 unstated-next(确保更新你所有的引入,这应该只是一个查找和替换的过程)。

尽管这是一个重大的 API 更改,我希望你尽可能轻松地完成此迁移。我正在使用最新的 React Hooks API ,为你进行优化,而不是使用原有的 Unstated.Container 代码。请随意提供有关如何做得更好的反馈。

首发 https://nusr.github.io/

你再也不用使用 Redux、Mobx、Flux 等状态管理了的更多相关文章

  1. Mobx | 强大的状态管理工具 | 可以用Mobx来替代掉redux

    来源简书 电梯直达 https://www.jianshu.com/p/505d9d9fe36a Mobx是一个功能强大,上手非常容易的状态管理工具.就连redux的作者也曾经向大家推荐过它,在不少情 ...

  2. redux沉思录:基于flux、状态管理、函数式编程的前端状态管理框架

    基于flux和reduce的通信和状态管理机制; 和数据库管理系统一样,redux是一个状态管理系统(或机制). const store = createStore( reducer, compose ...

  3. Flutter: MobX和flutter_mobx状态管理器

    MobX.dart网站上的 " 入门指南" mobxjs video 组织Stores 安装依赖 dependencies: mobx: flutter_mobx: dev_dep ...

  4. 微信小程序里使用 Redux 状态管理

    微信小程序里使用 Redux 状态管理 前言 前阵子一直在做小程序开发,采用的是官方给的框架 wepy , 如果还不了解的同学可以去他的官网查阅相关资料学习:不得不说的是,这个框架确相比于传统小程序开 ...

  5. 状态管理之 Flux、Redux、Vuex、MobX(概念篇)

    本文是对 Flux.Redux.Vuex.MobX 几种常用状态管理模式的总结,偏向于概念层面,不涉及过多代码. 状态管理 什么是状态管理? 状态管理就是,把组件之间需要共享的状态抽取出来,遵循特定的 ...

  6. Redux/Mobx/Akita/Vuex对比 - 选择更适合低代码场景的状态管理方案

    近期准备开发一个数据分析 SDK,定位是作为数据中台向外输出数据分析能力的载体,前端的功能表现类似低代码平台的各种拖拉拽.作为中台能力的载体,SDK 未来很大概率会需要支持多种视图层框架,比如Vue2 ...

  7. 妈妈再也不用担心别人问我是否真正用过redis了

    1. Memcache与Redis的区别 1.1. 存储方式不同 1.2. 数据支持类型 1.3. 使用底层模型不同 2. Redis支持的数据类型 3. Redis的回收策略 4. Redis小命令 ...

  8. 有了这个,再也不用每次连新机器都要设置secure crt属性了

    我连服务器用的是secure crt,每次ssh新服务器的时候都得手动设置字符编码和背景颜色,今天问了旁边的开发原来可以全局设置,以后连服务器的时候就再也不用手动设置相关属性了.步骤如下: 一开始点击 ...

  9. 锋利的js之妈妈再也不用担心我找错钱了

    用js实现收银功能. <!DOCTYPE html> <html xmlns="http://www.w3.org/1999/xhtml"> <hea ...

随机推荐

  1. 2015-2016-2《Java程序设计》团队博客1

    项目内容 经过一些讨论之后决定了最终的项目:简易画图板项目设计与开发 目标 制作一个能够画各种图形的画板,并能有多种颜色可以选择:输入文本时也可以选择字体:能够保存,新建,和导入图片 实现计划 十一周 ...

  2. Python plot_surface(Axes3D)方法:绘制3D图形

    3D 图形需要的数据与等高线图基本相同:X.Y 数据决定坐标点,Z 轴数据决定 X.Y 坐标点对应的高度.与等高线图使用等高线来代表高度不同,3D 图形将会以更直观的形式来表示高度. 为了绘制 3D ...

  3. Redis4.0新特性(一)-Memory Command

    Redis4.0版本增加了很多诱人的新特性,在redis精细化运营管理中都非常有用(猜想和antirez加入redislabs有很大关系):此系列几篇水文主要介绍以下几个新特性的使用和效果. Redi ...

  4. python的subprocess模块介绍

    一.subprocess以及常用的封装函数运行python的时候,我们都是在创建并运行一个进程.像Linux进程那样,一个进程可以fork一个子进程,并让这个子进程exec另外一个程序.在Python ...

  5. web编辑器的使用比较

    一开始测试的时候发现都还可以,只是出于后续考虑,选择了ckeditor,但是有时候会莫名出现Cannot read property 'XXX' of undefined,如下: Uncaught T ...

  6. vue---数据列表过滤筛选

    使用vue进行数据过滤筛选是比较常用的功能,常见的使用场景就是搜索框数据筛选过滤了.简单示例: <template> <div> <input type="te ...

  7. 【linux基础】ubuntu16.04 安装后,屏幕分辨率过低且不可调节

    前言 安装CUDA和NVIDIA驱动之后,屏幕分辨率过低且不可调节,尝试了几种方法都没成功解决,特此讲解决方法记录下来. 解决过程 注销进入登录界面,按ctrl+alt+F1进入命令行终端,输入账户名 ...

  8. Github-Dorks与辅助工具

    前言 Github搜索功能非常强大且有用,可用于在开源出来的Github仓库中搜索敏感数据.可以找到敏感的个人和/或组织信息(例如私钥,凭据,身份验证令牌等). 文中的github dork列表可以在 ...

  9. 切换普通用户报 -bash: fork: retry: No child processes

    ssh 连接普通用户 报这个错误 -bash: fork: retry: No child processes 解决办法: 更改vi /etc/security/limits.d/20-nproc.c ...

  10. node + promise 实现文件读写

    const fs = require('fs'); const promise = new Promise((resolve, reject) => {     fs.open('./c.txt ...