react-state-monad 0.0.1 → 1.0.3

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