跳至主要內容

反衝 0.0.10

·8 分鐘閱讀

反衝 0.0.9 和 0.0.10 現正推出,具備一些修正程式、TypeScript 支援以及 反衝快照 的全新 API,以觀察、檢查和管理全域反衝原子狀態。再次感謝協助讓這項工作成為可能的人,敬請期待更多精彩的進展。

錯誤修正

  • 伺服器端呈現修正程式,儘管我們尚未正式支援。(#233, #220, #284) - 感謝 @fyber-LJX、@Chrischuck 和 @aulneau
  • 修正部分情況下循序記錄依存關係訂閱的選擇器 (#296) - 感謝 @drarmstr
  • 修正在 useRecoilCallback() 中更新程式取得目前狀態 (#260) - 感謝 @drarmstr
  • 修正開源建置中拋出特定錯誤時的錯誤訊息。(#199) - 感謝 @jonthomp
  • 減少開源版本建置的 Flow 錯誤 (#308) - 感謝 @Komalov

改善

  • 拋出具意義訊息的錯誤,如果使用者未於反衝鉤子中使用原子或選擇器 (#205) - 感謝 @alexandrzavalii
  • 改善測試 (#321, #318, #294, #262, #295) - 感謝 @aaronabramov、@Komalov、@mondaychen、@drarmstr 和 @tyler-mitchell
  • 改善開源建置 (#249, #203, #33) - 感謝 @tony-go、@acutmore 和 @jaredpalmer

TypeScript 支援

TypeScript 支援已納入反衝 GitHub 儲存庫,而非 DefinitelyTyped,以協助其與 API 保持同步。(#292 和 #339) - 感謝 @csantos42

反衝快照

#312、#311、#310、#309、#260、#259、#258、#257、#256 - 感謝 @drarmstr 和團隊其餘成員

我們在 Recoil 中引入 Snapshot 的概念。Snapshot 是 Recoil 原子的狀態快照,且不可變。這旨在標準化觀察、檢查和管理全局 Recoil 狀態及衍生狀態的 API。對於開發工具、全局狀態同步、歷史記錄和導覽來說,非常有用。

API

讀取快照

Snapshot 類別提供以下方法,用於取得個別 Recoil 原子和選擇器的值

class Snapshot {
getLoadable: <T>(RecoilValue<T>) => Loadable<T>;
getPromise: <T>(RecoilValue<T>) => Promise<T>;
...
}

在原子狀態方面,快照是唯讀的。可用於讀取原子狀態,並評估選擇器衍生狀態。對於非同步選擇器,可以使用 getPromise() 方法,等待評估值,因此您可以看到選擇器值如何根據靜態原子狀態設定。

轉換快照

有時候您可能想變異快照。即使快照是不可變的,但它們有方法可以使用一組轉換,將自己對映至一項新的不可變快照。對映方法會採用一個回呼,將 MutableSnapshot 傳遞給它,整個回呼過程中會變異 MutableSnapshot,而它最後會成為對映操作所傳回的新快照。

class Snapshot {
...
map: (MutableSnapshot => void) => Snapshot;
asyncMap: (MutableSnapshot => Promise<void>) => Promise<Snapshot>;
}

class MutableSnapshot {
set: <T>(RecoilState<T>, T | DefaultValue | (T => T | DefaultValue)) => void;
reset: <T>(RecoilState<T>) => void;
}

請注意,set()reset() 具有和回寫選擇器的 set() 函數所提供的回呼相同簽章。

範例

  const newSnapshot = snapshot.map(({set}) => set(myAtom, 42));

Hooks

Recoil 有以下 Hooks,可讓您使用快照

useRecoilSnapshot()

function useRecoilSnapshot(): Snapshot

您可以使用此 Hook,在繪製元件時同步取得目前狀態的快照。即使概念簡單,這個 Hook 會訂閱任何使用它的元件至任何 Recoil 狀態變更,因此始終使用目前狀態的快照進行繪製。因此,請小心使用這個 Hook。您可能想要用它的情況之一,是支援伺服器端繪製,當時您需要使用第一次繪製時進行同步狀態。在未來,我們可能會提供暫緩功能來提升效能。

範例

定義一個 <LinkToNewState> 元件,用帶有突變所套用當前狀態為基礎的 href 呈示一個 <a> 錨。在此範例中,uriFromSnapshot() 是一個使用者定義的函式,可用於編碼 URI 中的當前狀態,以便在載入網頁時還原。

function LinkToNewState() {
const snapshot = useRecoilSnapshot();
const newSnapshot = snapshot.map(({set}) => set(myAtom, 42));
return <a href={uriFromSnapshot(newSnapshot)}>Click Me!</a>;
}

這是簡化的範例。我們有一個協助程式,如我們瀏覽器記錄持久性程式庫中用於產生連結,它更具擴充性和最佳化,即將推出。例如,它將劫持按一下處理常式來更新區域狀態,而不需歷經瀏覽器記錄。

useRecoilCallback()

type CallbackInterface = {
snapshot: Snapshot,
gotoSnapshot: Snapshot => void,
set: <T>(RecoilState<T>, (T => T) | T) => void,
reset: <T>(RecoilState<T>) => void,
};

function useRecoilCallback<Args, Return>(
callback: CallbackInterface => (...Args) => ReturnValue,
deps?: $ReadOnlyArray<mixed>,
): (...Args) => ReturnValue

useRecoilCallback() 掛鉤類似於 React useCallback() 掛鉤用於產生一個回呼函式。但是,您並非只是提供一個輸入回呼函式,而是用一個提供回呼介面參數的函式對其進行包裝,讓您能夠存取 Snapshotset()/reset() 回呼函式來更新當前全域狀態。提供的 Snapshot 代表回呼函式呼叫時,而非最初建立回呼函式時的狀態。

注意:這是 API 中一個稍有變更的部分,但是我們仍在 Recoil 的 0.0.x 版本,尚未完全開始語意版本控管。

useRecoilCallback() 也採用一個可選的 deps 陣列參數來控制備忘。您可以擴充 react-hooks/exhaustive-deps 棉絮法則,以確保適當使用它。

使用 useRecoilCallback() 的一些動機

  • 非同步使用 Recoil 狀態,如果原子或選擇器已更新,而不會訂閱一個 React 元件以重新呈示。

  • 將昂貴的尋找遞延到一個您不希望在呈示時間執行的非同步動作。

  • 執行副作用,您希望同時讀取或寫入 Recoil 狀態。

  • 動態更新原子或選擇器,在呈示時間,我們可能不知道我們想要更新哪個原子或選擇器,因此我們無法使用 useSetRecoilState()

  • 於呈示前預先擷取

範例

按一下時,將評估一個昂貴選擇器的按鈕元件。

function ShowDetailsButton() {
const onClick = useRecoilCallback(({snapshot}) => async () => {
const data = await snapshot.getPromise(expensiveQuery);
showPopup(data);
});

return <button onClick={onClick}>Show Details</button>;
}

useRecoilTransactionObserver()

function useRecoilTransactionObserver_UNSTABLE(({
snapshot: Snapshot,
previousSnapshot: Snapshot,
}) => void)

此掛鉤訂閱一個回呼函式,以便在 Recoil 原子狀態發生變更時隨時執行。可能會在一個交易中將多個更新批次編組在一起。此掛鉤非常適合永久保留狀態變更、開發工具、建立記錄等用途。在未來,我們可能會允許訂閱特定條件或提供效能減緩。

偵錯觀察者範例

function DebugObserver() {
useRecoilTransactionObserver_UNSTABLE(({snapshot}) => {
window.myDebugState = {
a: snapshot.getLoadable(atomA).contents,
b: snapshot.getLoadable(atomB).contents,
};
});
return null;
}

useGotoRecoilState()

function useGotoRecoilSnapshot(): Snapshot => void

這個鉤子回傳一個回呼,該回呼將 Snapshot 作為參數並將更新目前的 <RecoilRoot> 狀態,以匹配此原子狀態。

時間旅行範例

狀態變更歷程範例清單,具有返回並復原先前狀態的功能。

function TimeTravelObserver() {
const [snapshots, setSnapshots] = useState([]);

useRecoilTransactionObserver_UNSTABLE(({snapshot}) => {
setSnapshots([...snapshots, snapshot]);
});

const gotoSnapshot = useGotoRecoilSnapshot();

return (
<ol>
{snapshots.map((snapshot, i) => (
<li key={i}>
Snapshot {i}
<button onClick={() => gotoSnapshot(snapshot)}>
Restore
</button>
</li>
)}
</ol>
);
}

狀態初始化

<RecoilRoot> 元件也有 initializeState 道具,可以用來初始化原子狀態。此道具採用一個函式,其參數是一個 MutableSnapshot,可以用來設定初始原子狀態。當你事先知道所有原子時,這會有助於載入持續的狀態。這對於伺服器端渲染很有用,其中狀態應在第一次渲染時同步設定。

範例

function MyApp() {
return (
<RecoilRoot
initializeState={({set}) => {
for (const [atom, value] of atoms) {
set(atom, value);
}
}}
>
<AppContents />
</RecoilRoot>
);
}

接下來是什麼?

快照讓我們可以觀察和同步全域狀態。但如果我們想要一個更細緻、可組合的系統來處理個別的原子,怎麼辦?我們正在研究 原子效應 的概念,用於在原子層級觀察和處理副作用。這將使持續狀態或雙向與可變儲存同步更容易。想想同步狀態與瀏覽器 URI 歷程、瀏覽器本機儲存、RESTful API 等。很快就會推出!

這裡介紹的 Snapshot API 讓我們可以檢查個別原子和選擇項目前的狀態。我們將擴充此 API,以檢查可用的節點集合並探索資料流程圖結構。這對開發工具建置而言非常強大。敬請期待!

當然囉,我們仍舊持續對應 React Concurrent Mode,並致力於改善速度、擴充性和記憶體管理。