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
代碼。請隨意提供有關如何作得更好的反饋。