Skip to content

Commit

Permalink
🚀 Initial Commit
Browse files Browse the repository at this point in the history
  • Loading branch information
aromalanil committed Feb 28, 2021
0 parents commit e2f73fb
Show file tree
Hide file tree
Showing 11 changed files with 395 additions and 0 deletions.
12 changes: 12 additions & 0 deletions .github/FUNDING.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
# These are supported funding model platforms

github: # Replace with up to 4 GitHub Sponsors-enabled usernames e.g., [user1, user2]
patreon: # Replace with a single Patreon username
open_collective: # Replace with a single Open Collective username
ko_fi: aromalanil
tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel
community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry
liberapay: # Replace with a single Liberapay username
issuehunt: # Replace with a single IssueHunt username
otechie: # Replace with a single Otechie username
custom: ['https://paypal.me/aromalanil','https://www.buymeacoffee.com/aromalanil']
30 changes: 30 additions & 0 deletions .github/ISSUE_TEMPLATE/bug_report.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
---
name: Bug report
about: "\U0001F41E Found a bug? Report it here."
title: ''
labels: bug
assignees: ''

---

**Short Title**
Give a short title for the issue

**Describe the bug**
A clear and concise description of what the bug is.

**To Reproduce**
Steps to reproduce the behavior:
1. Go to '...'
2. Click on '....'
3. Scroll down to '....'
4. See an error

**Expected behavior**
A clear and concise description of what you expected to happen.

**Screenshots**
If applicable, add screenshots to help explain your problem.

**Additional context**
Add any other context about the problem here.
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
node_modules
dist
3 changes: 3 additions & 0 deletions .npmignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
tsconfig.json
src
.github
15 changes: 15 additions & 0 deletions .prettierrc
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
{
"printWidth": 100,
"tabWidth": 2,
"singleQuote": true,
"trailingComma": "all",
"bracketSpacing": true,
"jsxBracketSameLine": true,
"parser": "flow",
"semi": true,
"useTabs": false,
"quoteProps": "as-needed",
"jsxSingleQuote": true,
"arrowParens": "always",
"endOfLine": "lf"
}
70 changes: 70 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
# 🦏 React Rhino

React Rhino is a simple yet powerful state management library for [React](https://http://reactjs.org/)

## Installation

```bash
# If you use npm:
npm install react-rhino

# Or if you use Yarn:
yarn add react-rhino
```

## Usage

### Step 1
1. Create a `states.js` file in your `src` folder
2. Import `createRhinoState` from `react-rhino`

```js
import createRhinoState from 'react-rhino'
```

3. Pass an object to the `createRhinoState` function where the keys uniquely identify the state and values will be the initial value of the corresponding state.

```js
const { RhinoProvider, useRhinoState } = createRhinoState({
name: "John Doe",
isLoggedIn: true
})
```
4. `export` the `RhinoProvider` and `useRhinoState` from the file

```js
export { RhinoProvider, useRhinoState }
```

### Step 2
1. Import the `RhinoProvider` from the file and wrap the `App` component with it.

```js
import { RhinoProvider } from './states.js`
import App from "./App";
const rootElement = document.getElementById("root");
ReactDOM.render(
<RootProvider>
<App />
</RootProvider>,
rootElement
);
```
### Step 3
1. Import `useRhinoState` from the `states.js` to whichever component you want to use your global state.
```js
import { useRhinoState } from './states.js`
```
2. `useRhinoState` shares similar syntax with `useState` function. The only difference is that you pass a key to the hook instead of the initial value.
```js
const [name,setName] = useRhinoState("name")
```

> Note: Here "name" is the key identifying the state in the object we passed to `createRhinoState` function.


## Author
[Aromal Anil](https://aromalanil.tech)
60 changes: 60 additions & 0 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

34 changes: 34 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
{
"name": "react-rhino",
"version": "1.0.0",
"description": "A simple state management library using React context",
"main": "dist/index.js",
"types": "dist/index.d.ts",
"scripts": {
"build": "tsc"
},
"repository": {
"type": "git",
"url": "git+https://github.com/aromalanil/react-rhino.git"
},
"keywords": [
"react-state",
"state-management",
"react-state-management",
"react-rhino",
"rhino"
],
"author": "Aromal Anil <[email protected]> (https://aromalanil.tech)",
"bugs": {
"url": "https://github.com/aromalanil/react-rhino/issues"
},
"homepage": "https://github.com/aromalanil/react-rhino#readme",
"license": "MIT",
"devDependencies": {
"typescript": "^4.2.2"
},
"dependencies": {
"@types/react": "^17.0.2",
"react": "^17.0.1"
}
}
51 changes: 51 additions & 0 deletions src/create-single-rhino-state.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
import * as React from 'react';
import { createContext, useContext, useState } from 'react';

export interface ProviderProps {
children: React.ReactChildren | React.ReactChild;
}

/**
*
* Function to create a global state using React Context API,
* which returns the ContextProvider and hooks to get the
* state and updater function
*
* @param initialValue Initial value of the state
*/
function createSingleRhinoState<T>(initialValue: T) {
//Creating Context for state value & state updater function
const ValueContext = React.createContext(initialValue);
const UpdaterContext = createContext<React.Dispatch<React.SetStateAction<T>>>(() => null);

/**
*
* React hook which returns the stateful value
* @return Global State
*/
const useStateValue = () => useContext(ValueContext);

/**
*
* React hook which returns a function to update the global state
* @return Global state updater function
*/
const useStateUpdate = () => useContext(UpdaterContext);

/**
*
* Provider Component which makes the state available to all the nested components
*/
const Provider = ({ children }: ProviderProps) => {
const [state, setState] = useState(initialValue);

return (
<ValueContext.Provider value={state}>
<UpdaterContext.Provider value={setState}>{children}</UpdaterContext.Provider>
</ValueContext.Provider>
);
};

return { Provider, useStateValue, useStateUpdate };
}
export default createSingleRhinoState;
106 changes: 106 additions & 0 deletions src/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,106 @@
import * as React from 'react';
import createSingleRhinoState, { ProviderProps } from './create-single-rhino-state';

interface StateObject {
[key: string]: any | (() => any);
}

interface StateList<T> {
provider: ({ children }: ProviderProps) => JSX.Element;
stateHook: () => T;
updaterHook: () => React.Dispatch<React.SetStateAction<T>>;
}

/**
*
* Function to creates global state using rhino
* @param globalStatesObject An Object containing all the global states to be created
* @example <caption>Sample Use of createRhinoState</caption>
* const { Provider, useRhinoState }=createRhinoState({user:"John Doe",isLoggedIn:true});
*/
const createRhinoState = (globalStatesObject: StateObject) => {
const stateListData: { [key: string]: StateList<any> } = {};

Object.keys(globalStatesObject).forEach((key) => {
const initialValue = globalStatesObject[key];
const { Provider, useStateValue, useStateUpdate } = createSingleRhinoState(initialValue);

stateListData[key] = {
provider: Provider,
stateHook: useStateValue,
updaterHook: useStateUpdate,
};
});

/**
*
* Provider Component which makes the states available to all the nested components
*/
const RhinoProvider = ({ children }: ProviderProps) => {
const stateList = Object.values(stateListData);
const listLength = stateList.length;

let RootProvider = ({ children }: ProviderProps) => <>{children}</>;

for (let i = 0; i < listLength; i++) {
const Provider = stateList[i].provider;
const OldRootProvider = RootProvider;
RootProvider = ({ children }) => (
<OldRootProvider>
<Provider>{children}</Provider>
</OldRootProvider>
);
}
return <RootProvider>{children}</RootProvider>;
};

/**
*
* React hook which returns the stateful value
* corresponding to the key provided
* @param key Key which represents the state
*/
const useRhinoValue = <T extends unknown>(key: string): T => {
if (key in stateListData) {
return stateListData[key].stateHook();
} else {
throw new Error(`${key} is an invalid key`);
}
};

/**
*
* React hook which returns a function to update the global state
* corresponding to the key provided
* @param key Key which represents the state
*/
const useSetRhinoState = <T extends unknown>(
key: string,
): React.Dispatch<React.SetStateAction<T>> => {
if (key in stateListData) {
return stateListData[key].updaterHook();
} else {
throw new Error(`${key} is an invalid key`);
}
};

/**
*
* React hook which returns the stateful value corresponding to the key
* provided and a function to update it
* @param key Key which represents the state
*/
const useRhinoState = <T extends unknown>(
key: string,
): [T, React.Dispatch<React.SetStateAction<T>>] => {
if (key in stateListData) {
return [useRhinoValue(key), useSetRhinoState(key)];
} else {
throw new Error(`${key} is an invalid key`);
}
};

return { RhinoProvider, useRhinoState, useRhinoValue, useSetRhinoState };
};

export default createRhinoState;
Loading

0 comments on commit e2f73fb

Please sign in to comment.