react-store-input 0.1.1 → 0.1.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.d.mts CHANGED
@@ -1,8 +1,10 @@
1
1
  export { Store, StoreSubscriber, useStore } from './use_store.mjs';
2
2
  export { useSelector } from './use_selector.mjs';
3
3
  export { useSubscribe } from './use_subscribe.mjs';
4
- export { Input, Select, StoreInputPropsWithStore, Textarea, useStoreInput } from './use_store_input.mjs';
5
- export { useStoreInputProps } from './use_store_input_props.mjs';
4
+ export { StoreControllerProps, useStoreController } from './use_store_controller.mjs';
5
+ export { useStoreInputWithName } from './use_store_input_with_name.mjs';
6
+ export { StoreInputProps, useStoreInput } from './use_store_input.mjs';
7
+ export { Input, Select, StoreComponentProps, StoreComponentPropsWithStore, Textarea, useStoreComponent } from './use_store_component.mjs';
6
8
  export { FormStore, useFormStore } from './use_form_store.mjs';
7
9
  export { deserialize, serialize } from './serialize.mjs';
8
10
  import 'react';
package/dist/index.d.ts CHANGED
@@ -1,8 +1,10 @@
1
1
  export { Store, StoreSubscriber, useStore } from './use_store.js';
2
2
  export { useSelector } from './use_selector.js';
3
3
  export { useSubscribe } from './use_subscribe.js';
4
- export { Input, Select, StoreInputPropsWithStore, Textarea, useStoreInput } from './use_store_input.js';
5
- export { useStoreInputProps } from './use_store_input_props.js';
4
+ export { StoreControllerProps, useStoreController } from './use_store_controller.js';
5
+ export { useStoreInputWithName } from './use_store_input_with_name.js';
6
+ export { StoreInputProps, useStoreInput } from './use_store_input.js';
7
+ export { Input, Select, StoreComponentProps, StoreComponentPropsWithStore, Textarea, useStoreComponent } from './use_store_component.js';
6
8
  export { FormStore, useFormStore } from './use_form_store.js';
7
9
  export { deserialize, serialize } from './serialize.js';
8
10
  import 'react';
package/dist/index.js CHANGED
@@ -38,8 +38,10 @@ __export(index_exports, {
38
38
  useFormStore: () => useFormStore,
39
39
  useSelector: () => useSelector,
40
40
  useStore: () => useStore,
41
+ useStoreComponent: () => useStoreComponent,
42
+ useStoreController: () => useStoreController,
41
43
  useStoreInput: () => useStoreInput,
42
- useStoreInputProps: () => useStoreInputProps,
44
+ useStoreInputWithName: () => useStoreInputWithName,
43
45
  useSubscribe: () => useSubscribe
44
46
  });
45
47
  module.exports = __toCommonJS(index_exports);
@@ -115,14 +117,52 @@ function useSelector(store, selector, compare = (a, b) => a === b) {
115
117
  return state;
116
118
  }
117
119
 
120
+ // src/use_store_controller.tsx
121
+ var import_react4 = require("react");
122
+ function useStoreController(store, props) {
123
+ const ref = (0, import_react4.useRef)(null);
124
+ const dispatchKey = (0, import_react4.useId)();
125
+ (0, import_react4.useEffect)(() => {
126
+ const element = ref.current;
127
+ if (!element) {
128
+ return;
129
+ }
130
+ if (typeof props.ref === "function") {
131
+ props.ref(element);
132
+ } else if (props.ref) {
133
+ props.ref.current = element;
134
+ }
135
+ }, []);
136
+ useSubscribe(store, (state, key) => {
137
+ if (key === dispatchKey) {
138
+ return;
139
+ }
140
+ if (!ref.current) return;
141
+ props.onSubscribe(state, ref.current);
142
+ });
143
+ const onChange = () => {
144
+ store.dispatch(
145
+ (state) => {
146
+ const element = ref.current;
147
+ if (!element) return;
148
+ props.onDispatch(state, element);
149
+ },
150
+ {
151
+ key: dispatchKey
152
+ }
153
+ );
154
+ };
155
+ return {
156
+ dispatchKey,
157
+ ref,
158
+ onChange
159
+ };
160
+ }
161
+
118
162
  // src/use_store_input.tsx
119
163
  var import_react5 = require("react");
120
- var import_react6 = __toESM(require("react"));
121
-
122
- // src/use_store_input_props.tsx
123
- var import_react4 = require("react");
124
164
  var import_date_fns = require("date-fns");
125
- function useStoreInputProps(store, props) {
165
+ function useStoreInput(store, props) {
126
166
  const toInputValue = (value) => {
127
167
  if (value === void 0 || value === null) {
128
168
  return "";
@@ -144,7 +184,7 @@ function useStoreInputProps(store, props) {
144
184
  if (props.type === "checkbox" || props.type === "radio") {
145
185
  return void 0;
146
186
  }
147
- return toInputValue(store.state[props.name]);
187
+ return toInputValue(props.getter(store.state));
148
188
  };
149
189
  const toInputChecked = (value) => {
150
190
  if (props.type === "radio") {
@@ -159,103 +199,93 @@ function useStoreInputProps(store, props) {
159
199
  if (props.type !== "checkbox" && props.type !== "radio") {
160
200
  return void 0;
161
201
  }
162
- return toInputChecked(store.state[props.name]);
202
+ return toInputChecked(props.getter(store.state));
163
203
  };
164
- function useSubscriptionRef() {
165
- const ref2 = (0, import_react4.useRef)(null);
166
- (0, import_react4.useEffect)(() => {
167
- const input = ref2.current;
168
- if (!input) {
169
- return;
170
- }
171
- if (typeof props.ref === "function") {
172
- props.ref(input);
173
- } else if (props.ref) {
174
- props.ref.current = input;
175
- }
176
- return store.subscribe((state, key) => {
177
- if (key === dispatchKey) {
178
- return;
179
- }
180
- if (props.type === "checkbox" || props.type === "radio") {
181
- const checked = toInputChecked(state[props.name]);
182
- if (input.checked === checked) {
183
- return;
184
- }
185
- input.checked = checked;
186
- } else {
187
- const value = toInputValue(state[props.name]);
188
- if (input.value === value) {
189
- return;
190
- }
191
- input.value = value;
192
- }
193
- const event = new Event("input", { bubbles: true });
194
- input.dispatchEvent(event);
195
- });
196
- }, []);
197
- return ref2;
198
- }
199
204
  function toStateValue(value) {
200
- if (typeof store.state[props.name] === "number") {
205
+ const selected = props.getter(store.state);
206
+ if (typeof selected === "number") {
201
207
  return Number(value);
202
208
  }
203
- if (store.state[props.name] instanceof Date) {
209
+ if (selected instanceof Date) {
204
210
  return new Date(value);
205
211
  }
206
212
  return value;
207
213
  }
208
- function createChangeEventHandler() {
209
- return (e) => {
210
- const target = e.target;
211
- if (props.type === "checkbox") {
212
- store.dispatch(
213
- (state) => {
214
- state[props.name] = target.checked;
215
- },
216
- {
217
- key: dispatchKey
218
- }
219
- );
214
+ const inputProps = useStoreController(store, {
215
+ ref: props.ref,
216
+ onSubscribe: (state, element) => {
217
+ if ("checked" in element && (props.type === "checkbox" || props.type === "radio")) {
218
+ const checked = toInputChecked(props.getter(state));
219
+ if (element.checked === checked) {
220
+ return;
221
+ }
222
+ element.checked = checked;
220
223
  } else {
221
- store.dispatch(
222
- (state) => {
223
- state[props.name] = toStateValue(target.value);
224
- },
225
- {
226
- key: dispatchKey
227
- }
228
- );
224
+ const value = toInputValue(props.getter(state));
225
+ if (element.value === value) {
226
+ return;
227
+ }
228
+ element.value = value;
229
229
  }
230
- props.onChange?.(e);
231
- };
232
- }
233
- const dispatchKey = (0, import_react4.useId)();
234
- const ref = useSubscriptionRef();
235
- const name = String(props.name);
236
- const defaultValue = getDefaultValue();
237
- const defaultChecked = getDefaultChecked();
238
- const onChange = createChangeEventHandler();
230
+ const event = new Event("input", { bubbles: true });
231
+ element.dispatchEvent(event);
232
+ },
233
+ onDispatch: (state, element) => {
234
+ if ("checked" in element && props.type === "checkbox") {
235
+ props.setter(state, element.checked);
236
+ } else {
237
+ props.setter(state, toStateValue(element.value));
238
+ }
239
+ }
240
+ });
239
241
  return {
240
- key: dispatchKey,
241
- ref,
242
- name,
243
- defaultValue,
244
- defaultChecked,
245
- onChange
242
+ ref: inputProps.ref,
243
+ dispatchKey: inputProps.dispatchKey,
244
+ defaultValue: getDefaultValue(),
245
+ defaultChecked: getDefaultChecked(),
246
+ onChange: (event) => {
247
+ inputProps.onChange();
248
+ props.onChange?.(event);
249
+ }
246
250
  };
247
251
  }
248
252
 
249
- // src/use_store_input.tsx
250
- function useStoreInput(store) {
251
- const input = (0, import_react5.useCallback)(function Component(props) {
252
- return /* @__PURE__ */ import_react6.default.createElement(Input, { store, ...props });
253
+ // src/use_store_input_with_name.tsx
254
+ function useStoreInputWithName(store, props) {
255
+ const inputProps = useStoreInput(store, {
256
+ ...props,
257
+ getter: (state) => {
258
+ if (props.getter) {
259
+ return props.getter(state);
260
+ }
261
+ return state[props.name];
262
+ },
263
+ setter: (state, value) => {
264
+ if (props.setter) {
265
+ props.setter(state, value);
266
+ return;
267
+ }
268
+ state[props.name] = value;
269
+ }
270
+ });
271
+ return {
272
+ ...inputProps,
273
+ name: "name" in props ? String(props.name) : void 0
274
+ };
275
+ }
276
+
277
+ // src/use_store_component.tsx
278
+ var import_react6 = require("react");
279
+ var import_react7 = __toESM(require("react"));
280
+ function useStoreComponent(store) {
281
+ const input = (0, import_react6.useCallback)(function Component(props) {
282
+ return /* @__PURE__ */ import_react7.default.createElement(Input, { store, ...props });
253
283
  }, []);
254
- const select = (0, import_react5.useCallback)(function Component(props) {
255
- return /* @__PURE__ */ import_react6.default.createElement(Select, { store, ...props });
284
+ const select = (0, import_react6.useCallback)(function Component(props) {
285
+ return /* @__PURE__ */ import_react7.default.createElement(Select, { store, ...props });
256
286
  }, []);
257
- const textarea = (0, import_react5.useCallback)(function Component(props) {
258
- return /* @__PURE__ */ import_react6.default.createElement(Textarea, { store, ...props });
287
+ const textarea = (0, import_react6.useCallback)(function Component(props) {
288
+ return /* @__PURE__ */ import_react7.default.createElement(Textarea, { store, ...props });
259
289
  }, []);
260
290
  return {
261
291
  input,
@@ -267,41 +297,41 @@ function Input({
267
297
  store,
268
298
  ...props
269
299
  }) {
270
- const storeProps = useStoreInputProps(store, props);
271
- return /* @__PURE__ */ import_react6.default.createElement("input", { ...props, ...storeProps });
300
+ const storeProps = useStoreInputWithName(store, props);
301
+ return /* @__PURE__ */ import_react7.default.createElement("input", { ...props, ...storeProps });
272
302
  }
273
303
  function Select({
274
304
  store,
275
305
  ...props
276
306
  }) {
277
- const storeProps = useStoreInputProps(store, props);
278
- return /* @__PURE__ */ import_react6.default.createElement("select", { ...props, ...storeProps });
307
+ const storeProps = useStoreInputWithName(store, props);
308
+ return /* @__PURE__ */ import_react7.default.createElement("select", { ...props, ...storeProps });
279
309
  }
280
310
  function Textarea({
281
311
  store,
282
312
  ...props
283
313
  }) {
284
- const storeProps = useStoreInputProps(store, props);
285
- return /* @__PURE__ */ import_react6.default.createElement("textarea", { ...props, ...storeProps });
314
+ const storeProps = useStoreInputWithName(store, props);
315
+ return /* @__PURE__ */ import_react7.default.createElement("textarea", { ...props, ...storeProps });
286
316
  }
287
317
 
288
318
  // src/create_render.tsx
289
- var import_react7 = __toESM(require("react"));
319
+ var import_react8 = __toESM(require("react"));
290
320
  function createRenderWithStore(store) {
291
321
  return function createRender(selector, compare) {
292
322
  function Component() {
293
323
  const result = useSelector(store, (state) => state, compare);
294
324
  const content = selector(result);
295
- return /* @__PURE__ */ import_react7.default.createElement(import_react7.default.Fragment, null, content);
325
+ return /* @__PURE__ */ import_react8.default.createElement(import_react8.default.Fragment, null, content);
296
326
  }
297
- return /* @__PURE__ */ import_react7.default.createElement(Component, null);
327
+ return /* @__PURE__ */ import_react8.default.createElement(Component, null);
298
328
  };
299
329
  }
300
330
 
301
331
  // src/use_form_store.tsx
302
332
  function useFormStore(initialState) {
303
333
  const store = useStore(initialState);
304
- const storeInput = useStoreInput(store);
334
+ const storeComponent = useStoreComponent(store);
305
335
  return {
306
336
  get state() {
307
337
  return store.state;
@@ -309,7 +339,7 @@ function useFormStore(initialState) {
309
339
  dispatch: store.dispatch,
310
340
  subscribe: store.subscribe,
311
341
  render: createRenderWithStore(store),
312
- ...storeInput
342
+ ...storeComponent
313
343
  };
314
344
  }
315
345
 
@@ -409,7 +439,9 @@ function deserialize(data) {
409
439
  useFormStore,
410
440
  useSelector,
411
441
  useStore,
442
+ useStoreComponent,
443
+ useStoreController,
412
444
  useStoreInput,
413
- useStoreInputProps,
445
+ useStoreInputWithName,
414
446
  useSubscribe
415
447
  });
package/dist/index.mjs CHANGED
@@ -69,20 +69,52 @@ function useSelector(store, selector, compare = (a, b) => a === b) {
69
69
  return state;
70
70
  }
71
71
 
72
- // src/use_store_input.tsx
73
- import {
74
- useCallback
75
- } from "react";
76
- import React from "react";
72
+ // src/use_store_controller.tsx
73
+ import { useEffect as useEffect2, useId, useRef as useRef2 } from "react";
74
+ function useStoreController(store, props) {
75
+ const ref = useRef2(null);
76
+ const dispatchKey = useId();
77
+ useEffect2(() => {
78
+ const element = ref.current;
79
+ if (!element) {
80
+ return;
81
+ }
82
+ if (typeof props.ref === "function") {
83
+ props.ref(element);
84
+ } else if (props.ref) {
85
+ props.ref.current = element;
86
+ }
87
+ }, []);
88
+ useSubscribe(store, (state, key) => {
89
+ if (key === dispatchKey) {
90
+ return;
91
+ }
92
+ if (!ref.current) return;
93
+ props.onSubscribe(state, ref.current);
94
+ });
95
+ const onChange = () => {
96
+ store.dispatch(
97
+ (state) => {
98
+ const element = ref.current;
99
+ if (!element) return;
100
+ props.onDispatch(state, element);
101
+ },
102
+ {
103
+ key: dispatchKey
104
+ }
105
+ );
106
+ };
107
+ return {
108
+ dispatchKey,
109
+ ref,
110
+ onChange
111
+ };
112
+ }
77
113
 
78
- // src/use_store_input_props.tsx
79
- import {
80
- useEffect as useEffect2,
81
- useId,
82
- useRef as useRef2
83
- } from "react";
114
+ // src/use_store_input.tsx
115
+ import "react";
84
116
  import { format } from "date-fns";
85
- function useStoreInputProps(store, props) {
117
+ function useStoreInput(store, props) {
86
118
  const toInputValue = (value) => {
87
119
  if (value === void 0 || value === null) {
88
120
  return "";
@@ -104,7 +136,7 @@ function useStoreInputProps(store, props) {
104
136
  if (props.type === "checkbox" || props.type === "radio") {
105
137
  return void 0;
106
138
  }
107
- return toInputValue(store.state[props.name]);
139
+ return toInputValue(props.getter(store.state));
108
140
  };
109
141
  const toInputChecked = (value) => {
110
142
  if (props.type === "radio") {
@@ -119,95 +151,87 @@ function useStoreInputProps(store, props) {
119
151
  if (props.type !== "checkbox" && props.type !== "radio") {
120
152
  return void 0;
121
153
  }
122
- return toInputChecked(store.state[props.name]);
154
+ return toInputChecked(props.getter(store.state));
123
155
  };
124
- function useSubscriptionRef() {
125
- const ref2 = useRef2(null);
126
- useEffect2(() => {
127
- const input = ref2.current;
128
- if (!input) {
129
- return;
130
- }
131
- if (typeof props.ref === "function") {
132
- props.ref(input);
133
- } else if (props.ref) {
134
- props.ref.current = input;
135
- }
136
- return store.subscribe((state, key) => {
137
- if (key === dispatchKey) {
138
- return;
139
- }
140
- if (props.type === "checkbox" || props.type === "radio") {
141
- const checked = toInputChecked(state[props.name]);
142
- if (input.checked === checked) {
143
- return;
144
- }
145
- input.checked = checked;
146
- } else {
147
- const value = toInputValue(state[props.name]);
148
- if (input.value === value) {
149
- return;
150
- }
151
- input.value = value;
152
- }
153
- const event = new Event("input", { bubbles: true });
154
- input.dispatchEvent(event);
155
- });
156
- }, []);
157
- return ref2;
158
- }
159
156
  function toStateValue(value) {
160
- if (typeof store.state[props.name] === "number") {
157
+ const selected = props.getter(store.state);
158
+ if (typeof selected === "number") {
161
159
  return Number(value);
162
160
  }
163
- if (store.state[props.name] instanceof Date) {
161
+ if (selected instanceof Date) {
164
162
  return new Date(value);
165
163
  }
166
164
  return value;
167
165
  }
168
- function createChangeEventHandler() {
169
- return (e) => {
170
- const target = e.target;
171
- if (props.type === "checkbox") {
172
- store.dispatch(
173
- (state) => {
174
- state[props.name] = target.checked;
175
- },
176
- {
177
- key: dispatchKey
178
- }
179
- );
166
+ const inputProps = useStoreController(store, {
167
+ ref: props.ref,
168
+ onSubscribe: (state, element) => {
169
+ if ("checked" in element && (props.type === "checkbox" || props.type === "radio")) {
170
+ const checked = toInputChecked(props.getter(state));
171
+ if (element.checked === checked) {
172
+ return;
173
+ }
174
+ element.checked = checked;
180
175
  } else {
181
- store.dispatch(
182
- (state) => {
183
- state[props.name] = toStateValue(target.value);
184
- },
185
- {
186
- key: dispatchKey
187
- }
188
- );
176
+ const value = toInputValue(props.getter(state));
177
+ if (element.value === value) {
178
+ return;
179
+ }
180
+ element.value = value;
189
181
  }
190
- props.onChange?.(e);
191
- };
192
- }
193
- const dispatchKey = useId();
194
- const ref = useSubscriptionRef();
195
- const name = String(props.name);
196
- const defaultValue = getDefaultValue();
197
- const defaultChecked = getDefaultChecked();
198
- const onChange = createChangeEventHandler();
182
+ const event = new Event("input", { bubbles: true });
183
+ element.dispatchEvent(event);
184
+ },
185
+ onDispatch: (state, element) => {
186
+ if ("checked" in element && props.type === "checkbox") {
187
+ props.setter(state, element.checked);
188
+ } else {
189
+ props.setter(state, toStateValue(element.value));
190
+ }
191
+ }
192
+ });
199
193
  return {
200
- key: dispatchKey,
201
- ref,
202
- name,
203
- defaultValue,
204
- defaultChecked,
205
- onChange
194
+ ref: inputProps.ref,
195
+ dispatchKey: inputProps.dispatchKey,
196
+ defaultValue: getDefaultValue(),
197
+ defaultChecked: getDefaultChecked(),
198
+ onChange: (event) => {
199
+ inputProps.onChange();
200
+ props.onChange?.(event);
201
+ }
206
202
  };
207
203
  }
208
204
 
209
- // src/use_store_input.tsx
210
- function useStoreInput(store) {
205
+ // src/use_store_input_with_name.tsx
206
+ function useStoreInputWithName(store, props) {
207
+ const inputProps = useStoreInput(store, {
208
+ ...props,
209
+ getter: (state) => {
210
+ if (props.getter) {
211
+ return props.getter(state);
212
+ }
213
+ return state[props.name];
214
+ },
215
+ setter: (state, value) => {
216
+ if (props.setter) {
217
+ props.setter(state, value);
218
+ return;
219
+ }
220
+ state[props.name] = value;
221
+ }
222
+ });
223
+ return {
224
+ ...inputProps,
225
+ name: "name" in props ? String(props.name) : void 0
226
+ };
227
+ }
228
+
229
+ // src/use_store_component.tsx
230
+ import {
231
+ useCallback
232
+ } from "react";
233
+ import React from "react";
234
+ function useStoreComponent(store) {
211
235
  const input = useCallback(function Component(props) {
212
236
  return /* @__PURE__ */ React.createElement(Input, { store, ...props });
213
237
  }, []);
@@ -227,21 +251,21 @@ function Input({
227
251
  store,
228
252
  ...props
229
253
  }) {
230
- const storeProps = useStoreInputProps(store, props);
254
+ const storeProps = useStoreInputWithName(store, props);
231
255
  return /* @__PURE__ */ React.createElement("input", { ...props, ...storeProps });
232
256
  }
233
257
  function Select({
234
258
  store,
235
259
  ...props
236
260
  }) {
237
- const storeProps = useStoreInputProps(store, props);
261
+ const storeProps = useStoreInputWithName(store, props);
238
262
  return /* @__PURE__ */ React.createElement("select", { ...props, ...storeProps });
239
263
  }
240
264
  function Textarea({
241
265
  store,
242
266
  ...props
243
267
  }) {
244
- const storeProps = useStoreInputProps(store, props);
268
+ const storeProps = useStoreInputWithName(store, props);
245
269
  return /* @__PURE__ */ React.createElement("textarea", { ...props, ...storeProps });
246
270
  }
247
271
 
@@ -261,7 +285,7 @@ function createRenderWithStore(store) {
261
285
  // src/use_form_store.tsx
262
286
  function useFormStore(initialState) {
263
287
  const store = useStore(initialState);
264
- const storeInput = useStoreInput(store);
288
+ const storeComponent = useStoreComponent(store);
265
289
  return {
266
290
  get state() {
267
291
  return store.state;
@@ -269,7 +293,7 @@ function useFormStore(initialState) {
269
293
  dispatch: store.dispatch,
270
294
  subscribe: store.subscribe,
271
295
  render: createRenderWithStore(store),
272
- ...storeInput
296
+ ...storeComponent
273
297
  };
274
298
  }
275
299
 
@@ -368,7 +392,9 @@ export {
368
392
  useFormStore,
369
393
  useSelector,
370
394
  useStore,
395
+ useStoreComponent,
396
+ useStoreController,
371
397
  useStoreInput,
372
- useStoreInputProps,
398
+ useStoreInputWithName,
373
399
  useSubscribe
374
400
  };
@@ -1,9 +1,9 @@
1
1
  import { CreateRender } from './create_render.mjs';
2
2
  import { Store } from './use_store.mjs';
3
- import { useStoreInput } from './use_store_input.mjs';
3
+ import { useStoreComponent } from './use_store_component.mjs';
4
4
  import 'react';
5
5
 
6
- type FormStore<TState> = Store<TState> & ReturnType<typeof useStoreInput<TState>> & {
6
+ type FormStore<TState> = Store<TState> & ReturnType<typeof useStoreComponent<TState>> & {
7
7
  render: CreateRender<TState>;
8
8
  };
9
9
  declare function useFormStore<TState>(initialState: TState): FormStore<TState>;
@@ -1,9 +1,9 @@
1
1
  import { CreateRender } from './create_render.js';
2
2
  import { Store } from './use_store.js';
3
- import { useStoreInput } from './use_store_input.js';
3
+ import { useStoreComponent } from './use_store_component.js';
4
4
  import 'react';
5
5
 
6
- type FormStore<TState> = Store<TState> & ReturnType<typeof useStoreInput<TState>> & {
6
+ type FormStore<TState> = Store<TState> & ReturnType<typeof useStoreComponent<TState>> & {
7
7
  render: CreateRender<TState>;
8
8
  };
9
9
  declare function useFormStore<TState>(initialState: TState): FormStore<TState>;