コンテンツにスキップ

createState

getter および setter メソッドを備えたリアクティブな状態コンテナを作成します。 Observable による状態管理のシンプルなソリューションを提供します。

const [state, setState, { updateState, resetState }] = createState(0);
const stateHistory: number[] = [];
state.subscribe((value: number) => {
stateHistory.push(value);
});
assert.deepStrictEqual(stateHistory, [0]);
setState(10); // logs: 10
assert.deepStrictEqual(stateHistory, [0, 10]);
updateState((prev: number) => prev + 1); // logs: 11
assert.deepStrictEqual(stateHistory, [0, 10, 11]);
resetState(); // logs: 0
assert.deepStrictEqual(stateHistory, [0, 10, 11, 0]);

等しい更新を伝えない(equals)

Section titled “等しい更新を伝えない(equals)”

第 2 引数で equals オプションを指定できます。

const [filters, setFilters] = createState(
{ label: 'bug', page: 1 },
{ equals: fastDeepEqual },
);
const before = filters.getSnapshot().value;
// Derived state is recomputed only when the state really changes.
const query = filters.pipe(
map(({ label, page }) => `label:${label} page:${page}`),
);
const queryHistory: string[] = [];
query.subscribe((value: string) => {
queryHistory.push(value);
});
setFilters({ label: 'bug', page: 1 }); // equal: nothing is passed on
assert.strictEqual(filters.getSnapshot().value, before);
setFilters({ label: 'bug', page: 2 });
assert.deepStrictEqual(queryHistory, ['label:bug page:1', 'label:bug page:2']);

equals は現在の状態と次の状態を受け取ります。true を返すと、その更新は伝わりません。購読者にも、状態から派生したものにも通知されず、現在の状態がその同一性ごと保たれます(setState・updateState・resetState は次の状態ではなく現在の状態を返します)。下流の skipIfNoChange ではなく状態を持つ側で決めるのは、ここで伝わった更新は、オペレーターが間引く前に状態を直接読むものへ届いてしまうためです。

equals の既定値は Object.is で、すでに持っている値を書き込んでも何も伝わりません。等しいかどうかにかかわらずすべての更新を伝えるには equals: () => false を渡してください。

変わらなかった部分を保つ(keepEqualParts)

Section titled “変わらなかった部分を保つ(keepEqualParts)”

定期的に問い合わせたサーバーの返答のように、毎回新しいオブジェクトで丸ごと書き込む状態には keepEqualParts: true を指定できます。

type PullReport = DeepReadonly<{
summary: { open: number };
pulls: { number: number; title: string }[];
}>;
const [report, setReport] = createState<PullReport>(
{ summary: { open: 0 }, pulls: [] },
{ keepEqualParts: true },
);
const summary = report.pipe(map((r) => r.summary));
const summaryHistory: unknown[] = [];
summary.subscribe((value) => {
summaryHistory.push(value);
});
// A server answers each poll with the whole report, a new object every
// time. What did not change keeps the object already held.
setReport({
summary: { open: 1 },
pulls: [{ number: 1, title: 'one' }],
});
const held = report.getSnapshot().value;
// An answer that changes nothing is not passed on at all.
setReport({
summary: { open: 1 },
pulls: [{ number: 1, title: 'one' }],
});
assert.strictEqual(report.getSnapshot().value, held);
// An edited title: a new report and a new array, while the summary is the
// one held, so `summary` is not told of it.
setReport({
summary: { open: 1 },
pulls: [{ number: 1, title: 'one (edited)' }],
});
assert.notStrictEqual(report.getSnapshot().value, held);
assert.strictEqual(report.getSnapshot().value.summary, held.summary);
assert.deepStrictEqual(summaryHistory, [{ open: 0 }, { open: 1 }]);

次の状態は書き込む前に今の状態と比べられます。今の状態の同じ部分と深く等しい部分はその部分に置き換わるので、変わっていない部分を読む側は手元のオブジェクトを持ち続け、Object.is で比べる map がそこで止めます。何も変えない更新はそもそも伝わりません。配列の要素は index で照合し、keepEqualParts: { key } を渡すと key で照合します。keepEqualParts オペレーターの書き込み側の版で、こちらを優先してください。

View source on GitHub