Skip to content

Commit 93dca22

Browse files
committed
Release(Beta): See Changelog for details
0 parents  commit 93dca22

File tree

941 files changed

+970140
-0
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

941 files changed

+970140
-0
lines changed

CHANGELOG.md

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
# 🧤 React Hands
2+
3+
React's Own Hands Touching State The Easiest Way!
4+
5+
> By Hussein Kizz, Beta Release
6+
7+
## React Hands, 29-Mar-2023 (v1.0.0)
8+
9+
- Added: typescript naming convention, $typeName
10+
- Added: main reactState hook can be called without providing initialState and actions, useful when only interested in dispatch and retrieving state
11+
- Added: Now users will import types from main library itself
12+
- Fix: Store provider has a typescript error!
13+
- Todo: Explore Middleware Concept!
14+
- Todo: Add More Stuff, Persistent State etc
15+
16+
## React Hands, 30-Mar-2023 (v1.0.0)
17+
18+
- Fixed: Store provider has a typescript error!
19+
- Chore: Changed state record type from string and unknown to any!
20+
- Boost: Removed Spread operator in middleware implementation to improve code performance
21+
- Todo: Add better error handler to let user know when action function name differs from dispatched one, at line with flag --line 1.0 in codebase!
22+
- Boost: Used useCallback hook in compose function implementation to improve performance
23+
- Done: Fixed Tsc Errors
24+
- Done: Set development environment for compiling and packaging
25+
- Done: Test package installation and usage locally before publishing to npm!
26+
- Done: published first beta version, via Js Kampala Open Source!

README.md

Lines changed: 154 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,154 @@
1+
# 🧤 React Hands
2+
3+
> By Hussein Kizz, First Beta Release v1.0.0
4+
5+
React's Own Hands Touching State The Easiest Way!
6+
7+
Unlike others, react hands focuses on easiness and takes a shorthand approach to managing state in your react applications by using react's built in hooks mainly useContext and useReducer which are pretty handy by the way, and also emphasize a single source of truth or global state philosophy to allow managing state at scale a breeze, though local state approach is also supported intuitively. And with that said, `react-hands` is a lightweight, simple and easiest to use state management library to help you manage your application's state without having to learn that much anything new, as the library provides a `StoreProvider` wrapper component and a `useStore` hook for accessing and updating the state just as you would almost do it with react itself, resulting into a simillar and easy to use state management pattern.
8+
9+
## Installation
10+
11+
You can install the `react-hands` library using npm or yarn:
12+
13+
```bash
14+
npm install react-hands
15+
# or
16+
yarn add react-hands
17+
```
18+
19+
## Usage
20+
21+
### Creating the Store
22+
23+
To create the store, use the `reactState` function. It takes three arguments:
24+
25+
1. `initialState`: an object representing the initial state of your application's state.
26+
2. `actions`: an object containing functions that update the state in response to dispatched actions.
27+
3. `middlewares` (optional): an array of middleware functions that modifies the dispatch behavior.
28+
29+
> 💡Middlewares basically help you mutate state with a custom fucntion or an array of functions in given order, before the dispatched action takes place, this will be available in next release.
30+
31+
Meanwhile here is how to create a store:
32+
33+
``` jsx
34+
// App.jsx
35+
36+
import { reactState } from "react-hands";
37+
38+
const initialState = { count: 0 };
39+
40+
const actions = {
41+
increment: (state, action) => ({ count: state.count + 1 }),
42+
decrement: (state, action) => ({ count: state.count - 1 }),
43+
};
44+
45+
```
46+
47+
### Providing the Store
48+
49+
Wrap your top-level component, app or layout with the `StoreProvider` component to provide the store to your app and to all it's child components.
50+
51+
```jsx
52+
// App.jsx
53+
54+
const { StoreProvider } = reactState(initialState, actions);
55+
56+
function App() {
57+
return (
58+
<StoreProvider>
59+
<MyComponent />
60+
</StoreProvider>
61+
);
62+
}
63+
```
64+
65+
### Accessing the State
66+
67+
To access the state in your components, use the `useStore` hook. It returns an array with the current state and the dispatch function, simillar to useState hook in react!
68+
69+
```jsx
70+
// MyComponent.jsx
71+
72+
import { reactState } from "react-hands";
73+
74+
const { useStore } = reactState();
75+
76+
function MyComponent() {
77+
const [state, dispatch] = useStore();
78+
79+
return (
80+
<div>
81+
<p>Count: {state.count}</p>
82+
<button onClick={() => dispatch({ type: "increment" })}>+</button>
83+
<button onClick={() => dispatch({ type: "decrement" })}>-</button>
84+
</div>
85+
);
86+
}
87+
```
88+
89+
💡 Notice: The dispatch type such as `increment` should match the same state name in store such as `increment` exactly, otherwise react hands will blow an error in console, so watchout and let's continue!
90+
91+
### Updating the State
92+
93+
To update the state, dispatch an action to the store. The action is an object with a `type` property that corresponds to one of the functions in the `actions` object. For example here both the increment and decrement actions increase and decrease the count in store respectively!
94+
95+
```jsx
96+
const actions = {
97+
increment: (state, action) => ({ count: state.count + 1 }),
98+
decrement: (state, action) => ({ count: state.count - 1 }),
99+
};
100+
101+
// dispatching action...
102+
dispatch({ type: "increment" });
103+
```
104+
105+
### Handling Errors
106+
107+
If an action with an unrecognized type is dispatched, `react hands` will log some error to the console and return the current state as is. The error message will usually be reminding you to use the same name as the state in your dispatch action. And more error handlers are being worked on!
108+
109+
### 🔥 Bonus
110+
111+
Did you know, react-hands supports typescript out of the box, it also provides types for state and dispatch, you just have to import a type like `$typeName` from react hands like this for example:
112+
113+
```jsx
114+
// MyApp.tsx
115+
116+
import { $Action, $State, reactState } from "react-hands";
117+
118+
export default function MyApp() {
119+
const initialState = {
120+
isDarkMode: false,
121+
count: 0,
122+
};
123+
124+
const actions = {
125+
toggleDarkMode: (state: $State) => ({
126+
...state,
127+
isDarkMode: !state.isDarkMode,
128+
}),
129+
increment: (state: $State) => ({
130+
...state,
131+
count: state.count + 1,
132+
}),
133+
decrement: (state: $State) => ({
134+
...state,
135+
count: state.count - 1,
136+
}),
137+
};
138+
139+
const { StoreProvider } = reactState(initialState, actions);
140+
141+
return (
142+
<StoreProvider>
143+
<MyComponent/>
144+
</StoreProvider>
145+
);
146+
}
147+
148+
```
149+
150+
## Conclusion
151+
152+
The `react-hands` library provides a simple way to manage state in your React applications. It's easy to use, and its lightweight nature makes it a great option for react projects small or complex. It's still work in progress, therefore contributions are welcomed, we will be updating this documentation to explore other patterns such as adding items to store from any component other than main or top level component, persisting state depsite page reloads etc.
153+
154+
😇 Pro Tip: If it works don't tocuh it, or break that comfort zone and give this a try!!!

dist/index.tsx

Lines changed: 106 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,106 @@
1+
// React Hands
2+
// All exportable tpypes have naming convention $typeName
3+
import * as React from 'react';
4+
5+
const { createContext, useContext, useReducer, useMemo, useCallback } = React;
6+
7+
export type $State = Record<string, any>;
8+
export type $Action = { type: string; payload?: unknown };
9+
export type $Actions = Record<
10+
string,
11+
(state: $State, action: $Action) => $State
12+
>;
13+
14+
export type $Middleware = (store: $Store) => (next: $Dispatch) => $Dispatch;
15+
export type $Store = { state: $State; dispatch: $Dispatch };
16+
export type $Dispatch = (action: $Action) => void;
17+
18+
type StoreType = {
19+
StoreProvider: React.FC<{ children: React.ReactNode }>;
20+
useStore: () => [$State, $Dispatch];
21+
};
22+
23+
const StoreContext = createContext<$State | undefined>(undefined);
24+
25+
export function reactState(
26+
initialState: $State = {},
27+
actions: $Actions = {},
28+
middlewares: $Middleware[] = []
29+
): StoreType {
30+
function reducer(state: $State, action: $Action) {
31+
const handler = actions[action.type];
32+
if (handler) {
33+
return handler(state, action);
34+
} else {
35+
// handle error then return state as is -- line 1.0
36+
console.log(
37+
`Bad Hands: Your probably passed a different reference to dispatch, "${action.type}" should match the same state name referenced in state!`
38+
);
39+
return state;
40+
}
41+
}
42+
43+
function applyMiddlewares(
44+
middlewares: $Middleware[],
45+
store: $Store
46+
): $Dispatch {
47+
const middlewareAPI = {
48+
state: store.state,
49+
dispatch: (action: $Action) => newDispatch(action),
50+
};
51+
const functionsChainArray = middlewares.map((middleware) =>
52+
middleware(middlewareAPI)
53+
);
54+
const newDispatch = compose(functionsChainArray)(store.dispatch);
55+
return newDispatch;
56+
}
57+
58+
function compose(funcs: ((arg: any) => any)[]) {
59+
return useCallback(
60+
function composed(arg: any) {
61+
let result = arg;
62+
for (let i = funcs.length - 1; i >= 0; i--) {
63+
result = funcs[i](result);
64+
}
65+
return result;
66+
},
67+
[funcs]
68+
);
69+
}
70+
71+
const StoreProvider: React.FC<{ children: React.ReactNode }> = ({
72+
children,
73+
}) => {
74+
const [state, dispatch] = useReducer(reducer, initialState);
75+
const enhancedDispatch = applyMiddlewares(middlewares, {
76+
state,
77+
dispatch,
78+
});
79+
const memorizedValue = useMemo(
80+
() => ({ state, dispatch: enhancedDispatch }),
81+
[state]
82+
);
83+
84+
return (
85+
<StoreContext.Provider value={memorizedValue}>
86+
{children}
87+
</StoreContext.Provider>
88+
);
89+
};
90+
91+
function useStore(): [$State, $Dispatch] {
92+
const context = useContext<$State | undefined>(StoreContext);
93+
94+
// tell user to wrap their app in store provider
95+
if (context === undefined) {
96+
throw new Error(
97+
'Bad Hands: Your top level component or app must be wrapped within the StoreProvider!'
98+
);
99+
}
100+
101+
const { state, dispatch } = context;
102+
return [state as $State, dispatch as $Dispatch];
103+
}
104+
105+
return { StoreProvider, useStore };
106+
}

node_modules/.bin/acorn

Lines changed: 1 addition & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

node_modules/.bin/mkdirp

Lines changed: 1 addition & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

node_modules/.bin/resolve

Lines changed: 1 addition & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

node_modules/.bin/rimraf

Lines changed: 1 addition & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

node_modules/.bin/tree-kill

Lines changed: 1 addition & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

node_modules/.bin/ts-node

Lines changed: 1 addition & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

node_modules/.bin/ts-node-cwd

Lines changed: 1 addition & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)