Unstated Next README 的中文翻译html
这个库的做者但愿使用 React 内置 API ,直接实现状态管理的功能。看完这个库的说明后,没有想到代码能够这个玩。短短几行代码,仅仅使用 React Hooks ,就实现了状态管理的功能。react
看完以后,第一想法就是翻译成中文,分享给其余人。提交 Pull Request 后,库做者将个人翻译合并了。同时做者欢迎将 README 翻译成其余语言,如下是所有翻译内容,不妥之处欢迎指正或 Pull Request.git
永远没必要再考虑 React 状态管理了,仅仅 200 字节的状态管理解决方案。github
可是,最重要的问题是:这比 Redux 更好吗? 答案多是。npm
你本身看着办吧!dom
npm install --save unstated-next
复制代码
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")) 复制代码
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 文档。ide
首先,使用 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 类型是完善的,那么类型都会自动推断。
由于咱们只使用了自定义 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 }
}
复制代码
这对于保持 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 }
}
复制代码
unstated-next
无需优化。全部你要作的优化,都是标准的 React 优化。
优化前:
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>
)
}
复制代码
优化前:
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>
)
}
复制代码
优化前:
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 精神的继承者。由于我相信 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@2
和 unstated-next
。 当你完成迁移后,你能够更新到 unstated@3
,同时删除 unstated-next
(确保更新你全部的引入,这应该只是一个查找和替换的过程)。
尽管这是一个重大的 API 更改,我但愿你尽量轻松地完成此迁移。我正在使用最新的 React Hooks API ,为你进行优化,而不是使用原有的 Unstated.Container
代码。请随意提供有关如何作得更好的反馈。