Skip to content

Commit

Permalink
Version 2.0 (#4)
Browse files Browse the repository at this point in the history
* Make Provider and hooks directly available from package

* 2.0.0-beta.0

* Remove Providers from stateDataList

* Update documentation for version 2.0

* Make version 2.0 stable
  • Loading branch information
aromalanil authored Aug 20, 2021
1 parent 862fb83 commit 81cedbd
Show file tree
Hide file tree
Showing 11 changed files with 259 additions and 214 deletions.
209 changes: 104 additions & 105 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ yarn add react-rhino
## Why choose Rhino?

### 🏋️‍♂️ Lightweight
Only **~560** bytes when Minified & Gzipped.
Only **~770** bytes when Minified & Gzipped.

### 🐱‍🏍 Syntax you already know
Offers a straightforward syntax similar to the built-in hooks of React.
Expand All @@ -37,58 +37,44 @@ Get started with Rhino in a short amount of time.

## Usage

### Getting started
Set up state management for your application following these three easy steps shown in the example. Or see a [demo project](https://codesandbox.io/s/react-rhino-example-svv5b) showing `react-rhino` in use.
Set up React Rhino in your project using these simple steps

### Step 1
Wrap your app with `RhinoProvider` and pass the initialStates object to it

1. #### Create global state
To get started, create a file called `states.js`. This file holds a declaration of the global state for the entire app.
> Note: `initialStates` is an object in which each entries will become a global state. In this object key will be the identifier for the state and value will be the initial value of that state.
`states.js`
```jsx
import createRhinoState from "react-rhino";
import { RhinoProvider } from 'react-rhino';

const { RhinoProvider, useRhinoState, useRhinoValue, useSetRhinoState } = createRhinoState({
name: "John Doe", // Will become a new global state
dark_mode: true // Will become another new global state
});

export { RhinoProvider, useRhinoState, useRhinoValue, useSetRhinoState }
```

2. #### Wrapping App with RhinoProvider
After creating and initializing state for app(in step described above), import `RhinoProvider` inside the top-level `index.js` file of the app. Wrap `<App/>` with `<RhinoProvider>` to make state and updater functions available to child components in the app.
const initialStates = {
dark_mode: true
}

`index.js`
```jsx
import { RhinoProvider } from "./states.js"
import App from "./App";

const rootElement = document.getElementById("root");
ReactDOM.render(
<RhinoProvider>
<App />
</RhinoProvider>,
rootElement
);
function App() {
return (
<RhinoProvider initialStates={initialStates}>
<Counter />
</RhinoProvider>
);
}
```

3. #### To consume global state
To consume the global state, import the `useRhinoState` variable from the component file that requires the use of the global state.
> Use the array destructuring syntax to pluck out: a constant holding a state value and an updater function to update the same state value from the global state.
### Step 2
Consume global state in any of your components, by using the `useRhinoState` hook as in the below example

`dark_mode.js`
```jsx
import { useRhinoState } from "./states.js"
import { useRhinoState } from "react-rhino"

const DarkModeButton = () => {

/* "dark_mode" is the key to identify the state */
/* "dark_mode" is the key given to the state in initialStates */
const [isDarkMode, setDarkMode] = useRhinoState("dark_mode");

const toggleDarkMode = () => {
setDarkMode(currentMode => !currentMode);
}

return(
<p>{ isDarkMode ? "Switch to Light" : "Switch to dark" }</p>
<button onClick={toggleDarkMode}>Toggle</button>
Expand All @@ -98,12 +84,67 @@ import { useRhinoState } from "./states.js"
export default DarkModeButton;
```

### A Component which only reads the state and not the updater function
For Components that only reads the state values, declare a constant inside the component and assign the constant to a call to `useRhinoValue("key_to_identify_state_value")` passing in key as an argument like:
> Note : `useRhinoState` is similar to the `useState` hook of React. `useRhinoState` will also returns an array containing the state and it's updator function.<br/><br/>
The only difference is that unlike
`useState` hook we pass the key of the global state to `useRhinoState` hook.


## API / Documentation
### RhinoProvider
Components that use Rhino state need `RhinoProvider` to appear somewhere in the parent tree. A good place to put this is in your root component.

> `RhinoProvider` takes only a single prop, `initialStates`. Each entries in `initialStates` will be converted in to a global state. <br/> <br/>Each key represents a global state and the corresponding values of the keys will become the initial value of the state.
```js
import { RhinoProvider } from "react-rhino";

const initialStates={
name: "John Doe", // Will become a global state with initial value "John Doe"
isDarkMode : true, // Will become another global state with initial value true
}

function App() {
return (
<RhinoProvider initialStates={initialStates}>
<SearchBar />
</RhinoProvider>
);
}
```

### useRhinoState
Takes key representing the state as argument and returns an array with the state value and the function to update the state.

> This hook is pretty similar to `useState` hook in React.
```jsx
const [darkMode, setDarkMode] = useRhinoState("isDarkMode");
/* Here "isDakMode" is the key representing the state */
```

### useRhinoValue
Takes key representing the state as argument and returns only the the state value.
> You can use this if your component only needs to read the state but perform no updates.
```jsx
const darkMode = useRhinoValue("isDarkMode");
/* Here "isDakMode" is the key representing the state */
```

### useSetRhinoState
Takes key representing the state as argument and returns the function to update the state.
> You can use this if your component only needs the updater function and not the state itself.
Having an updater function in the component will not trigger a rerender on the state change.
```jsx
const setDarkMode = useRhinoValue("isDarkMode");
/* Here "isDakMode" is the key representing the state */
```

## Some Examples
### A Component which only read the state and not the updater function
For Components that only read the state values, declare a constant inside the component and assign the constant to a call to `useRhinoValue("key_to_identify_state_value")` passing in key as an argument like:

`menu_bar.js`
```jsx
import { useRhinoValue } from "./states.js"
import { useRhinoValue } from "react-rhino"

const Menu = () => {

Expand All @@ -129,7 +170,7 @@ Declare a constant inside the component and assign the constant to a call to `us
`toggle.js`
> This component will not rerender if the state `isDarkMode` changes as it only uses the updater function and not the state itself.
```jsx
import { useSetRhinoState } from "./states.js"
import { useSetRhinoState } from "react-rhino"

const Toggle = () => {

Expand All @@ -151,7 +192,7 @@ Accessing multiple state values is pretty straight forward, declare constants to
`details.js`

```jsx
import { useRhinoValue } from "./states.js"
import { useRhinoValue } from "react-rhino"

const Datails= () => {
/*
Expand All @@ -168,73 +209,31 @@ const Datails= () => {
}
```

## API / Documentation
`createRhinoState` is the only function you can directly import from the package. All other API elements are returned from this function.

### createRhinoState()
Takes a single object as argument in which each global states as its entries.

Here each key represents each state with their corresponding values being their initial value.

```jsx
import createRhinoState from "react-rhino";

const { RhinoProvider, useRhinoState } = createRhinoState({
name: "John Doe", // Will create a global state with initial value "John Doe"
isDarkMode : true, // Will create another global state with initial value true
});

export { RhinoProvider, useRhinoState }
```

> `createRhinoState` will output `RhinoProvider` , `useRhinoState`,
`useRhinoValue` and `useSetRhinoState` hooks


### RhinoProvider
Components that use Rhino state need `RhinoProvider` to appear somewhere in the parent tree. A good place to put this is in your root component.

```js
import { RhinoProvider } from "./states.js";

function App() {
return (
<RhinoProvider>
<SearchBar />
</RhinoProvider>
);
}
```

### useRhinoState
Takes key representing state object as input and returns an array with the state value and the function to update the state.

> This hook is pretty similar to `useState` hook in React.
```jsx
const [darkMode, setDarkMode] = useRhinoState("isDarkMode");
/* Here "isDakMode" is the key representing the state */
```

### useRhinoValue
Takes key representing state object as input and returns the state value.
> You can use this if your component only needs to read the state but perform no updates.
```jsx
const darkMode = useRhinoValue("isDarkMode");
/* Here "isDakMode" is the key representing the state */
```

### useSetRhinoState
Takes key representing state object as input and returns the function to update the state.
> You can use this if your component only needs the updater function and not the state itself.
Having an updater function in the component will not trigger a rerender on the state change.
```jsx
const setDarkMode = useRhinoValue("isDarkMode");
/* Here "isDakMode" is the key representing the state */
```

## Author
[Aromal Anil](https://aromalanil.tech)

## License
MIT
```
MIT License
Copyright (c) 2021 Aromal Anil
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
```

2 changes: 1 addition & 1 deletion package-lock.json

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

2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "react-rhino",
"version": "1.3.0",
"version": "2.0.0",
"description": "A simple global state management library for React.js",
"main": "dist/index.js",
"types": "dist/index.d.ts",
Expand Down
54 changes: 54 additions & 0 deletions src/RhinoProvider.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
import * as React from 'react';
import { createContext, useContext } from 'react';
import { IProvider, ProviderProps } from './types';
import createGlobalState from './create-global-state';
import CombineProviders from './utils/CombineProviders';

interface StateObject {
[key: string]: any;
}

interface StateData<T> {
stateHook: () => T;
updaterHook: () => React.Dispatch<React.SetStateAction<T>>;
}

interface StateDataList {
[key: string]: StateData<any>;
}

interface RhinoProviderProps extends ProviderProps {
initialStates: StateObject;
}

const StateDataContext = createContext<StateDataList>({});

const RhinoProvider: React.FC<RhinoProviderProps> = ({
children,
initialStates: initialStatesObject,
}: RhinoProviderProps) => {
const stateDataList: StateDataList = {};
const ProviderList: Array<IProvider> = [];

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

stateDataList[key] = {
stateHook: useStateValue,
updaterHook: useStateUpdate,
};

ProviderList.push(Provider);
});

return (
<CombineProviders providers={ProviderList}>
<StateDataContext.Provider value={stateDataList}>{children}</StateDataContext.Provider>
</CombineProviders>
);
};

export const useStateDataList = () => useContext(StateDataContext);

export default RhinoProvider;
Original file line number Diff line number Diff line change
@@ -1,9 +1,6 @@
import * as React from 'react';
import { createContext, useContext, useState } from 'react';

export interface ProviderProps {
children: React.ReactChild | React.ReactChild[];
}
import { ProviderProps } from './types';

/**
*
Expand All @@ -13,7 +10,7 @@ export interface ProviderProps {
*
* @param initialValue Initial value of the state
*/
function createSingleRhinoState<T>(initialValue: T) {
function createGlobalState<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);
Expand Down Expand Up @@ -48,4 +45,4 @@ function createSingleRhinoState<T>(initialValue: T) {

return { Provider, useStateValue, useStateUpdate };
}
export default createSingleRhinoState;
export default createGlobalState;
20 changes: 20 additions & 0 deletions src/hooks/useRhinoState.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
import { useStateDataList } from '../RhinoProvider';
import useSetRhinoState from './useSetRhinoState';
import useRhinoValue from './useRhinoValue';

/**
*
* 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
*/
function useRhinoState<T>(key: string): [T, React.Dispatch<React.SetStateAction<T>>] {
const stateDataList = useStateDataList();

if (key in stateDataList) {
return [useRhinoValue(key), useSetRhinoState(key)];
}
throw new Error(`${key} is an invalid key`);
}

export default useRhinoState;
Loading

0 comments on commit 81cedbd

Please sign in to comment.