react-state-monad 1.0.8 → 1.0.10

Sign up to get free protection for your applications and to get access to all the features.
package/README.md CHANGED
@@ -1,252 +1,252 @@
1
- # React State Monad
2
-
3
- [![npm](https://img.shields.io/npm/v/react-state-monad)](https://www.npmjs.com/package/react-state-monad/)
4
- [![Build Status](https://img.shields.io/github/actions/workflow/status/alvmivan/react-state-monad/publish.yml?branch=main)](https://github.com/alvmivan/react-state-monad/releases/latest)
5
- [![License](https://img.shields.io/github/license/alvmivan/react-state-monad)](./LICENSE)
6
-
7
- A set of hooks to manage/transform/filter states with monads in React.
8
-
9
- ## Description
10
-
11
- `react-state-monad` provides a set of monadic state management utilities, specifically designed to work seamlessly with
12
- React's state hooks. It allows you to manage, transform, and filter states in a functional and declarative way using
13
- monads like `Maybe` and `Option`.
14
-
15
- This library leverages the power of monads to encapsulate state changes, enabling a cleaner, more predictable way to
16
- handle various state conditions in React.
17
-
18
- ## Features
19
-
20
- - Manage state using monads like `Maybe` and `Option`.
21
- - Simplify handling of undefined or null values in state.
22
- - Leverage functional programming patterns in React state management.
23
- - Support for TypeScript with full type definitions.
24
-
25
- ## Installation
26
-
27
- You can install `react-state-monad` using npm or yarn:
28
-
29
- ```bash
30
- npm install react-state-monad
31
- ```
32
-
33
- or
34
-
35
- ```bash
36
- yarn add react-state-monad
37
- ```
38
-
39
- ## Usage
40
-
41
- Here's an example of how you can use the hooks in your React components:
42
-
43
- ### `useStateObject<T>`
44
-
45
- This hook initializes a StateObject with the provided initial value. It uses React's useState hook to manage the
46
- internal state and returns a StateObject that represents the current state.
47
-
48
- Parameters: `initialState`: The initial value of the state. This value can be of any type, determined by the generic
49
- type `T`.
50
-
51
- Returns a `StateObject` of type `T` representing the initialized state. This `StateObject` is an instance
52
- of `ValidState`, which
53
- encapsulates the state value and provides a `setValue` function to update it.
54
-
55
- ### `useEmptyState<T>`
56
-
57
- This hook initializes a `StateObject` with an empty state. It is useful as a fallback when no valid state is available.
58
-
59
- Parameters: None.
60
-
61
- Returns a `StateObject` of type `T` representing an empty state. This `StateObject` is an instance of `EmptyState`,
62
- which encapsulates the absence of a state value.
63
-
64
- ### `useElementState<T>`
65
-
66
- This hook allows you to derive and update a specific element in an array within a `StateObject`. It is useful for
67
- managing state at a granular level within an array.
68
-
69
- Parameters:
70
-
71
- - `state`: The `StateObject` containing an array.
72
- - `index`: The index of the element to be derived.
73
-
74
- Returns a `StateObject` of type `T` representing the element at the given index. If the index is out of bounds or the
75
- state is empty, it returns an instance of `EmptyState`. Otherwise, it returns an instance of `ValidState`, which
76
- encapsulates the element value and provides a `setValue` function to update it.
77
-
78
- ### `useFieldState<TOriginal, TField>`
79
-
80
- This hook derives a field from the state object and creates a new `StateObject` for the field's value. It is useful for
81
- managing state at a granular level within an object.
82
-
83
- Parameters:
84
-
85
- - `state`: The `StateObject` containing the original state.
86
- - `field`: The field name to be derived from the state.
87
-
88
- Returns a `StateObject` of type `TField` representing the derived field. This `StateObject` is an instance
89
- of `ValidState`, which encapsulates the field value and provides a `setValue` function to update it.
90
-
91
- For example:
92
-
93
- ```jsx
94
- import React from 'react';
95
- import {useStateObject, useFieldState} from 'react-state-monad';
96
-
97
- const MyComponent = () => {
98
- const userState = useStateObject({
99
- name: 'John Doe',
100
- age: 30,
101
- });
102
-
103
- const nameState = useFieldState(userState, 'name');
104
- const ageState = useFieldState(userState, 'age');
105
-
106
- return (
107
- <div>
108
- <input
109
- type="text"
110
- value={nameState.value}
111
- onChange={(e) => nameState.value = e.target.value}
112
- />
113
- <input
114
- type="number"
115
- value={ageState.value}
116
- onChange={(e) => ageState.value = parseInt(e.target.value, 10)}
117
- />
118
- </div>
119
- );
120
- };
121
-
122
- export default MyComponent;
123
- ```
124
-
125
- ### `useRemapArray<T>`
126
-
127
- This hook maps each element in an array within a `StateObject` to a new `StateObject`, allowing for independent updates
128
- of each element while keeping the overall array state synchronized.
129
-
130
- Parameters:
131
-
132
- - `state`: The `StateObject` containing an array.
133
-
134
- Returns an array of new `StateObject`s, each representing an element in the original array. This allows individual
135
- updates while keeping the array state synchronized. If the state has no value, it returns an empty array.
136
-
137
- ### Complete Example
138
-
139
- Here's a more complete example demonstrating the usage of `useStateObject`, `useFieldState`, `useElementState`,
140
- and `useRemapArray` hooks in a React component:
141
-
142
- ```tsx
143
-
144
- const AgeField = (props: { ageState: StateObject<number> }) => <div>
145
- <label>Age:</label>
146
- <input
147
- type="number"
148
- value={props.ageState.value}
149
- onChange={x => props.ageState.value = parseInt(x.target.value, 10)}
150
- />
151
- </div>;
152
-
153
-
154
- const NameField = (props: { nameState: StateObject<string> }) => {
155
- return <div>
156
- <label>Name:</label>
157
- <input
158
- type="text"
159
- value={props.nameState.value}
160
- onChange={x => props.nameState.value = x.target.value}
161
- />
162
- </div>;
163
- }
164
-
165
- const HobbyField = (props: { hobbyState: StateObject<string> }) => {
166
- return <div>
167
- <input
168
- type="text"
169
- value={props.hobbyState.value}
170
- onChange={x => props.hobbyState.value = x.target.value}
171
- />
172
- </div>;
173
- }
174
-
175
- const HobbiesField = (props: { hobbiesState: StateObject<string[]> }) => {
176
-
177
- const hobbyStates: StateObject<string>[] = useRemapArray(props.hobbiesState);
178
-
179
- const addHobby = () => {
180
- // Always use the setter to update arrays, do not modify them directly to ensure React state consistency.
181
- // Immutability is key 💗
182
- props.hobbiesState.value = [...props.hobbiesState.value, ''];
183
- }
184
-
185
- return <div>
186
- <label>Hobbies:</label>
187
- {
188
- hobbyStates.map((hobbyState, index) => <HobbyField key={index} hobbyState={hobbyState}/>)
189
- }
190
- <button onClick={addHobby}>Add Hobby</button>
191
- </div>;
192
-
193
-
194
- };
195
-
196
- export const UserProfile = () => {
197
-
198
- type DudeData = {
199
- name: string;
200
- age: number;
201
- hobbies: string[];
202
- }
203
- // Initialize state with an object containing user details and an array of hobbies
204
- const userState: StateObject<DudeData> = useStateObject({
205
- name: 'John Doe',
206
- age: 30,
207
- hobbies: ['Reading', 'Traveling', 'Cooking'],
208
- });
209
-
210
- // Derive state for individual fields
211
- const nameState: StateObject<string> = useFieldState(userState, 'name');
212
- const ageState: StateObject<number> = useFieldState(userState, 'age');
213
-
214
- // Derive state for hobbies array
215
- const hobbiesState: StateObject<string[]> = useFieldState(userState, 'hobbies');
216
-
217
-
218
- return (
219
- <div>
220
- <h1>User Profile</h1>
221
- <NameField nameState={nameState}/>
222
- <AgeField ageState={ageState}/>
223
- <HobbiesField hobbiesState={hobbiesState}/>
224
- </div>
225
- );
226
- };
227
- ```
228
-
229
- ## Contributing
230
-
231
- Contributions are welcome! If you'd like to contribute to this library, please fork the repository and submit a pull
232
- request.
233
-
234
- How to Contribute
235
- Fork the repository.
236
-
237
- * Create a new branch for your feature `git checkout -b feature-name`
238
- * Commit your changes `git commit -am 'Add new feature'`
239
- * Push to the branch `git push origin feature-name`
240
- * Open a pull request. I'll be happy to review it!
241
-
242
- ## License
243
-
244
- This project is licensed under the GPL-3.0 License.
245
-
246
- ## Author
247
-
248
- `Marcos Alvarez`
249
-
250
- [<img src="https://github.githubassets.com/images/modules/logos_page/GitHub-Mark.png" width="38" height="38">](https://github.com/alvmivan)
251
- [<img src="https://www.linkedin.com/favicon.ico" width="40" height="40">](https://www.linkedin.com/in/marcos-alvarez-40651b150/)
1
+ # React State Monad
2
+
3
+ [![npm](https://img.shields.io/npm/v/react-state-monad)](https://www.npmjs.com/package/react-state-monad/)
4
+ [![Build Status](https://img.shields.io/github/actions/workflow/status/alvmivan/react-state-monad/publish.yml?branch=main)](https://github.com/alvmivan/react-state-monad/releases/latest)
5
+ [![License](https://img.shields.io/github/license/alvmivan/react-state-monad)](./LICENSE)
6
+
7
+ A set of hooks to manage/transform/filter states with monads in React.
8
+
9
+ ## Description
10
+
11
+ `react-state-monad` provides a set of monadic state management utilities, specifically designed to work seamlessly with
12
+ React's state hooks. It allows you to manage, transform, and filter states in a functional and declarative way using
13
+ monads like `Maybe` and `Option`.
14
+
15
+ This library leverages the power of monads to encapsulate state changes, enabling a cleaner, more predictable way to
16
+ handle various state conditions in React.
17
+
18
+ ## Features
19
+
20
+ - Manage state using monads like `Maybe` and `Option`.
21
+ - Simplify handling of undefined or null values in state.
22
+ - Leverage functional programming patterns in React state management.
23
+ - Support for TypeScript with full type definitions.
24
+
25
+ ## Installation
26
+
27
+ You can install `react-state-monad` using npm or yarn:
28
+
29
+ ```bash
30
+ npm install react-state-monad
31
+ ```
32
+
33
+ or
34
+
35
+ ```bash
36
+ yarn add react-state-monad
37
+ ```
38
+
39
+ ## Usage
40
+
41
+ Here's an example of how you can use the hooks in your React components:
42
+
43
+ ### `useStateObject<T>`
44
+
45
+ This hook initializes a StateObject with the provided initial value. It uses React's useState hook to manage the
46
+ internal state and returns a StateObject that represents the current state.
47
+
48
+ Parameters: `initialState`: The initial value of the state. This value can be of any type, determined by the generic
49
+ type `T`.
50
+
51
+ Returns a `StateObject` of type `T` representing the initialized state. This `StateObject` is an instance
52
+ of `ValidState`, which
53
+ encapsulates the state value and provides a `setValue` function to update it.
54
+
55
+ ### `useEmptyState<T>`
56
+
57
+ This hook initializes a `StateObject` with an empty state. It is useful as a fallback when no valid state is available.
58
+
59
+ Parameters: None.
60
+
61
+ Returns a `StateObject` of type `T` representing an empty state. This `StateObject` is an instance of `EmptyState`,
62
+ which encapsulates the absence of a state value.
63
+
64
+ ### `useElementState<T>`
65
+
66
+ This hook allows you to derive and update a specific element in an array within a `StateObject`. It is useful for
67
+ managing state at a granular level within an array.
68
+
69
+ Parameters:
70
+
71
+ - `state`: The `StateObject` containing an array.
72
+ - `index`: The index of the element to be derived.
73
+
74
+ Returns a `StateObject` of type `T` representing the element at the given index. If the index is out of bounds or the
75
+ state is empty, it returns an instance of `EmptyState`. Otherwise, it returns an instance of `ValidState`, which
76
+ encapsulates the element value and provides a `setValue` function to update it.
77
+
78
+ ### `useFieldState<TOriginal, TField>`
79
+
80
+ This hook derives a field from the state object and creates a new `StateObject` for the field's value. It is useful for
81
+ managing state at a granular level within an object.
82
+
83
+ Parameters:
84
+
85
+ - `state`: The `StateObject` containing the original state.
86
+ - `field`: The field name to be derived from the state.
87
+
88
+ Returns a `StateObject` of type `TField` representing the derived field. This `StateObject` is an instance
89
+ of `ValidState`, which encapsulates the field value and provides a `setValue` function to update it.
90
+
91
+ For example:
92
+
93
+ ```jsx
94
+ import React from 'react';
95
+ import {useStateObject, useFieldState} from 'react-state-monad';
96
+
97
+ const MyComponent = () => {
98
+ const userState = useStateObject({
99
+ name: 'John Doe',
100
+ age: 30,
101
+ });
102
+
103
+ const nameState = useFieldState(userState, 'name');
104
+ const ageState = useFieldState(userState, 'age');
105
+
106
+ return (
107
+ <div>
108
+ <input
109
+ type="text"
110
+ value={nameState.value}
111
+ onChange={(e) => nameState.value = e.target.value}
112
+ />
113
+ <input
114
+ type="number"
115
+ value={ageState.value}
116
+ onChange={(e) => ageState.value = parseInt(e.target.value, 10)}
117
+ />
118
+ </div>
119
+ );
120
+ };
121
+
122
+ export default MyComponent;
123
+ ```
124
+
125
+ ### `useRemapArray<T>`
126
+
127
+ This hook maps each element in an array within a `StateObject` to a new `StateObject`, allowing for independent updates
128
+ of each element while keeping the overall array state synchronized.
129
+
130
+ Parameters:
131
+
132
+ - `state`: The `StateObject` containing an array.
133
+
134
+ Returns an array of new `StateObject`s, each representing an element in the original array. This allows individual
135
+ updates while keeping the array state synchronized. If the state has no value, it returns an empty array.
136
+
137
+ ### Complete Example
138
+
139
+ Here's a more complete example demonstrating the usage of `useStateObject`, `useFieldState`, `useElementState`,
140
+ and `useRemapArray` hooks in a React component:
141
+
142
+ ```tsx
143
+
144
+ const AgeField = (props: { ageState: StateObject<number> }) => <div>
145
+ <label>Age:</label>
146
+ <input
147
+ type="number"
148
+ value={props.ageState.value}
149
+ onChange={x => props.ageState.value = parseInt(x.target.value, 10)}
150
+ />
151
+ </div>;
152
+
153
+
154
+ const NameField = (props: { nameState: StateObject<string> }) => {
155
+ return <div>
156
+ <label>Name:</label>
157
+ <input
158
+ type="text"
159
+ value={props.nameState.value}
160
+ onChange={x => props.nameState.value = x.target.value}
161
+ />
162
+ </div>;
163
+ }
164
+
165
+ const HobbyField = (props: { hobbyState: StateObject<string> }) => {
166
+ return <div>
167
+ <input
168
+ type="text"
169
+ value={props.hobbyState.value}
170
+ onChange={x => props.hobbyState.value = x.target.value}
171
+ />
172
+ </div>;
173
+ }
174
+
175
+ const HobbiesField = (props: { hobbiesState: StateObject<string[]> }) => {
176
+
177
+ const hobbyStates: StateObject<string>[] = useRemapArray(props.hobbiesState);
178
+
179
+ const addHobby = () => {
180
+ // Always use the setter to update arrays, do not modify them directly to ensure React state consistency.
181
+ // Immutability is key 💗
182
+ props.hobbiesState.value = [...props.hobbiesState.value, ''];
183
+ }
184
+
185
+ return <div>
186
+ <label>Hobbies:</label>
187
+ {
188
+ hobbyStates.map((hobbyState, index) => <HobbyField key={index} hobbyState={hobbyState}/>)
189
+ }
190
+ <button onClick={addHobby}>Add Hobby</button>
191
+ </div>;
192
+
193
+
194
+ };
195
+
196
+ export const UserProfile = () => {
197
+
198
+ type DudeData = {
199
+ name: string;
200
+ age: number;
201
+ hobbies: string[];
202
+ }
203
+ // Initialize state with an object containing user details and an array of hobbies
204
+ const userState: StateObject<DudeData> = useStateObject({
205
+ name: 'John Doe',
206
+ age: 30,
207
+ hobbies: ['Reading', 'Traveling', 'Cooking'],
208
+ });
209
+
210
+ // Derive state for individual fields
211
+ const nameState: StateObject<string> = useFieldState(userState, 'name');
212
+ const ageState: StateObject<number> = useFieldState(userState, 'age');
213
+
214
+ // Derive state for hobbies array
215
+ const hobbiesState: StateObject<string[]> = useFieldState(userState, 'hobbies');
216
+
217
+
218
+ return (
219
+ <div>
220
+ <h1>User Profile</h1>
221
+ <NameField nameState={nameState}/>
222
+ <AgeField ageState={ageState}/>
223
+ <HobbiesField hobbiesState={hobbiesState}/>
224
+ </div>
225
+ );
226
+ };
227
+ ```
228
+
229
+ ## Contributing
230
+
231
+ Contributions are welcome! If you'd like to contribute to this library, please fork the repository and submit a pull
232
+ request.
233
+
234
+ How to Contribute
235
+ Fork the repository.
236
+
237
+ * Create a new branch for your feature `git checkout -b feature-name`
238
+ * Commit your changes `git commit -am 'Add new feature'`
239
+ * Push to the branch `git push origin feature-name`
240
+ * Open a pull request. I'll be happy to review it!
241
+
242
+ ## License
243
+
244
+ This project is licensed under the GPL-3.0 License.
245
+
246
+ ## Author
247
+
248
+ `Marcos Alvarez`
249
+
250
+ [<img src="https://github.githubassets.com/images/modules/logos_page/GitHub-Mark.png" width="38" height="38">](https://github.com/alvmivan)
251
+ [<img src="https://www.linkedin.com/favicon.ico" width="40" height="40">](https://www.linkedin.com/in/marcos-alvarez-40651b150/)
252
252
  [<img src="https://ssl.gstatic.com/ui/v1/icons/mail/rfr/gmail.ico" width="40" height="40">](mailto:alvmivan@gmail.com)
package/index.cjs ADDED
@@ -0,0 +1,169 @@
1
+ "use strict";
2
+ var __defProp = Object.defineProperty;
3
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
+ var __getOwnPropNames = Object.getOwnPropertyNames;
5
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
6
+ var __export = (target, all) => {
7
+ for (var name in all)
8
+ __defProp(target, name, { get: all[name], enumerable: true });
9
+ };
10
+ var __copyProps = (to, from, except, desc) => {
11
+ if (from && typeof from === "object" || typeof from === "function") {
12
+ for (let key of __getOwnPropNames(from))
13
+ if (!__hasOwnProp.call(to, key) && key !== except)
14
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
15
+ }
16
+ return to;
17
+ };
18
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
19
+
20
+ // index.ts
21
+ var index_exports = {};
22
+ __export(index_exports, {
23
+ default: () => index_default,
24
+ useArrayState: () => useArrayState,
25
+ useElementState: () => useElementState,
26
+ useEmptyState: () => useEmptyState,
27
+ useFieldState: () => useFieldState,
28
+ useRemapArray: () => useRemapArray,
29
+ useStateObject: () => useStateObject
30
+ });
31
+ module.exports = __toCommonJS(index_exports);
32
+
33
+ // src/hooks/useFieldState.ts
34
+ function useFieldState(state, field) {
35
+ return state.map(
36
+ (original) => original[field],
37
+ // Extracts the field value.
38
+ (newField, original) => ({ ...original, [field]: newField })
39
+ // Updates the field with the new value.
40
+ );
41
+ }
42
+
43
+ // src/implementations/emptyState.ts
44
+ var EmptyState = class _EmptyState {
45
+ // No value stored, returns an error when accessed.
46
+ get value() {
47
+ throw new Error("Not implemented");
48
+ }
49
+ get hasValue() {
50
+ return false;
51
+ }
52
+ orElse(orElse) {
53
+ return orElse;
54
+ }
55
+ do() {
56
+ }
57
+ filter() {
58
+ return this;
59
+ }
60
+ set value(_) {
61
+ }
62
+ flatMap() {
63
+ return new _EmptyState();
64
+ }
65
+ map() {
66
+ return new _EmptyState();
67
+ }
68
+ };
69
+
70
+ // src/implementations/validState.ts
71
+ var ValidState = class _ValidState {
72
+ state;
73
+ setter;
74
+ constructor(state, setter) {
75
+ this.state = state;
76
+ this.setter = setter;
77
+ }
78
+ get value() {
79
+ return this.state;
80
+ }
81
+ do(action) {
82
+ action(this.state);
83
+ }
84
+ orElse() {
85
+ return this.state;
86
+ }
87
+ set value(newState) {
88
+ this.setter(newState);
89
+ }
90
+ map(mappingFunction, inverseMappingFunction) {
91
+ const derivedState = mappingFunction(this.state);
92
+ const derivedSetter = (newState) => {
93
+ this.setter(inverseMappingFunction(newState, this.state));
94
+ };
95
+ return new _ValidState(derivedState, derivedSetter);
96
+ }
97
+ flatMap(mappingFunction) {
98
+ return mappingFunction(this.state);
99
+ }
100
+ get hasValue() {
101
+ return true;
102
+ }
103
+ filter(predicate) {
104
+ return predicate(this.state) ? this : new EmptyState();
105
+ }
106
+ };
107
+
108
+ // src/hooks/useElementState.ts
109
+ function useElementState(state, index) {
110
+ if (!state.hasValue || index < 0 || index >= state.value.length) {
111
+ return new EmptyState();
112
+ }
113
+ return new ValidState(
114
+ state.value[index],
115
+ (newElement) => {
116
+ const arrayCopy = [...state.value];
117
+ arrayCopy[index] = newElement;
118
+ state.value = arrayCopy;
119
+ }
120
+ );
121
+ }
122
+
123
+ // src/hooks/useEmptyState.ts
124
+ function useEmptyState() {
125
+ return new EmptyState();
126
+ }
127
+
128
+ // src/hooks/useStateObject.ts
129
+ var import_react = require("react");
130
+ function useStateObject(initialState) {
131
+ const [state, setState] = (0, import_react.useState)(initialState);
132
+ return new ValidState(state, setState);
133
+ }
134
+
135
+ // src/hooks/useRemapArray.ts
136
+ function useRemapArray(state) {
137
+ if (!state.hasValue) return [];
138
+ const count = state.value.length;
139
+ const result = [];
140
+ for (let i = 0; i < count; i++) {
141
+ result.push(
142
+ new ValidState(
143
+ state.value[i],
144
+ // The current value of the element at index i.
145
+ (newElement) => {
146
+ const arrayCopy = [...state.value];
147
+ arrayCopy[i] = newElement;
148
+ state.value = arrayCopy;
149
+ }
150
+ )
151
+ );
152
+ }
153
+ return result;
154
+ }
155
+ function useArrayState(states) {
156
+ return useStateObject(states.filter((state) => state.hasValue).map((state) => state.value));
157
+ }
158
+
159
+ // index.ts
160
+ var index_default = void 0;
161
+ // Annotate the CommonJS export names for ESM import in node:
162
+ 0 && (module.exports = {
163
+ useArrayState,
164
+ useElementState,
165
+ useEmptyState,
166
+ useFieldState,
167
+ useRemapArray,
168
+ useStateObject
169
+ });