@cheprasov/react-global-state
v3.1.1
Published
The library allows to manage global state easy. It is based on React Context API and allows to pass states (values & set functions) to children components via Context.
Downloads
2
Maintainers
Readme
@cheprasov/react-global-state (v3.1.1)
The library allows to manage global state and nested global states easy. It is based on React Context API and allows to pass states (values & set functions) to children components via Context.
Features:
- The library uses React API for implementation the Global State features.
- Simple & Powerful work with Global State.
- Allows to have several Global States / nested Global States.
- It supports Functional & Class Components.
- Possibility to get/set values to Global States outside react components.
- It is possibly to subscribe on Global State updates.
- Supports client-side & server-side renders.
- Optimised for performance.
- Easy to learn and use.
- Written on TypeScript and supports types.
1. How to install
> npm install @cheprasov/react-global-state
import { createGlobalScope, useGlobalScope, Scope } from '@cheprasov/react-global-state';
2. If you know React then you almost know the library already.
2.1. Global Scope (Collection of Global States).
The library's function createGlobalScope(scope: Object)
allows to create multi states easy under a global scope. And then use hook useGlobalScope(name: string | Scope)
allows to use any state from the global scope like a separate state param.
Example: Creating a Global Scope
import { createGlobalScope, Scope } from '@cheprasov/react-global-state';
// The original object will not be changed
const rootScope = new Scope({
name: 'Alex',
city: 'London',
age: 37,
});
const GlobalScope = createGlobalScope(rootScope); // return React.FunctionComponent
const root = ReactDOM.createRoot(document.getElementByI('root'));
root.render(
<GlobalScope>
<App />
</GlobalScope>
);
Example: Using Global Scope
import { useGlobalScope } from '@cheprasov/react-global-state';
const User: React.FC = () => {
const user = useGlobalScope(); // use empty param for getting root scope
const [ name, setName ] = user.name; // like useState
const [ city, setCity] = user.city; // like useState
const [ age, setAge ] = user.age; // like useState
const increaseAge = () => {
// `set function` has the same API like a `set function` from React.useState()
setAge((value) => value + 1);
}
const useEffect(() => {
// Triggers when any state of the scope is changed
}, [user])
return (
<div>
Name: {name} <br />
City: {city} <br />
Age: {age} <button onClick={increaseAge}>+</button>
</div>
);
}
** Example:** GlobalScope at Class Components
import React from 'react';
import { withGlobalScope } from '@cheprasov/react-global-state';
class UserClass extends React.Component {
increaseAge = () => {
if (this.props.userGlobalScope) {
const [ , setAge ] = this.props.userGlobalScope.age;
setAge((value: number) => value + 1);
}
}
render() {
if (!this.props.userGlobalScope) {
return (
<div>
User Scope is not provided
</div>
);
}
const [ name ] = this.props.userGlobalScope.name; // like useState
const [ city ] = this.props.userGlobalScope.city; // like useState
const [ age ] = this.props.userGlobalScope.age; // like useState
return (
<div>
Name: {name} <br />
City: {city} <br />
Age: {age} <button onClick={this.increaseAge}>+</button> <br />
</div>
);
}
}
// Note, we use empty key for getting root scope
const UserClassWithGlobalScope = withGlobalScope(UserClass, { '': 'userGlobalScope' });
export { UserClassWithGlobalScope as UserClass };
2.2. Creating Nested Global Scopes / Multi Global Scopes.
The main idea of the library is operation with Nested Global Scopes. For example, you can created complicated structure of Global Scopes with nested Global Scopes.
Let's check the example:
const rootScopes = new Scope({
app: new Scope({
settings: new Scope({
priceType: 'total',
}),
user: new Scope({
name: 'Alex',
city: 'London',
age: 37,
hobby: { // Not a scope
chess: 'beginner',
it: 'expert',
},
}),
search: new Scope({
departure: 'London',
destination: 'Paris',
date: Date.now(),
rooms: [
{ adult: 2 },
],
nights: 7,
filters: new Scope({
rating: 5,
price: {
min: 0,
max: 1000,
},
}),
}),
}),
});
const GlobalScopes = createGlobalStates(rootScopes);
const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(
<GlobalScopes>
<App />
</GlobalScopes>
);
In the example below we created 6 global scopes: root, app
, settings
, user
, search
and filters
. As you can see global scope filters
is nested to global scope search
, and search
is nested to global scope app
. Also, global scopes user
and setting
are nested to app
scope too.
This nested model creates relationship between the global scopes. If any field in a scope is updated then the scope will be updated too, and as result all parent scopes will be updated too. Like a bubbling principle. When a state updates on an scope, it first updates the scope, then on its parent scope, then all the way up on other parent scopes.
For example. We can change desctination at search
scope.
const SomeComponent = () => {
// ...
const searchGlobalScope = useGlobalScope('app.search');
const [ destination, setDestionation ] = searchGlobalScope.destination;
// ...
setDestionation('Rome');
// ...
}
The destination
of global scope search
is update to Rome
, then whole global scope search
will be updated, and then global scope app
will be updated. Note, scopes filters
, user
and setting
will not be updated.
Example:
const App = () => {
// ...
const appScope = useGlobalScope('app');
const userScope = appGlobalScope.user; // or useGlobalScope('app.user');
const settingsScope = appGlobalScope.settings; // or useGlobalScope('app.settings');
const searchScope = appGlobalScope.search; // or useGlobalScope('app.search');
const filtersScope = appGlobalScope.filters; // or useGlobalScope('app.search.filters');
useEffect(() => {
// will be called if any state is updated at any of nested scopes
}, [appScope]);
useEffect(() => {
// will be called if any state of `user` scope is updated
}, [userScope]);
useEffect(() => {
// will be called if any state of `filters` scope is updated
}, [filtersScope]);
useEffect(() => {
// will be called if any state of `filters` or `search` scopes is updated
}, [searchScope]);
}
2.3. Demo examples
Please see more examples at demo folder.
3. Documentation
3.1. Global Scope
3.1.1. Creating a Global Scope
createGlobalScope(scope: Scope) // Returns React.memo(React.FunctionalComponent)
Please note, the createGlobalScope
should be called once and outside a component implementation.
Params:
- scope:
Scope
- A Scope object with scope sctucture.
Returns:
React.FunctionalComponent
(Wrapped by React.memo). It should be used inside your application (at any render level) for initialisation Global State feature for children components.
Examples:
const GlobalScope = createGlobalScope(new Scope({ name: 'Alex' }));
//...
root.render(
<GlobalScope>
<App />
</GlobalScope>
);
or
import { useGlobalScope } from '@cheprasov/react-global-state';
const GlobalScope = createGlobalScope(new Scope({ name: 'Alex' }));
const App: React.FC = () => {
// ...
return (
<GlobalScope>
All components here will be able to use the global scope
<div className="App">
<SomeComponents>
...
</SomeComponents>
</div>
</GlobalScope>
);
}
3.1.2. Reading / Updating Global Scope
useGlobalScope(name: string | Scope = '') // Returns Scope { [key: string]: [value, setValue function] }
The hook function useGlobalScope()
should be used inside a Functional Component for getting a scope object with value and set functions for each state in the scope.
Params:
- name:
string | Scope
- Name of scope. Use empty param for using root scope.
Returns:
ScopeVariablesWrapper
object likeRecord<string, [value, setValue function]}
. Or you could think about the scope like an object with keys and result of calluseState()
for each value:Record<string, useState(value)}
. Note, the returned wrapped scope object is always a new object if any of scope's values is updated.
Examples:
const userScope = new Scope({
name: 'Alex',
city: 'London',
age: 37,
});
const UserGlobalScope = createGlobalScope(userScope);
//...
root.render(
<UserGlobalScope>
<App />
</UserGlobalScope>
);
and then
import { useGlobalScope } from '@cheprasov/react-global-state';
const User: React.FC = () => {
const globalScope = useGlobalScope();
// globalScope = {
// name: ['Alex', setName],
// city: ['London', setCity],
// age: [37, setAge]
// }
// Or think about it like useState for each the scope object property
// globalScope = {
// name: useState('Alex'),
// city: useState('London'),
// age: useState(37)
// }
const [ name, setName ] = globalScope.name; // like useState
const [ city ] = globalScope.city; // like useState
const [ age, setAge ] = globalScope.age; // like useState
const increaseAge = () => {
// `set function` has the same API like a `set function` from React.useState()
setAge((value) => value + 1);
// or
// setAge(38);
}
useEffect(() => {
// shows message only if name is changed
console.log('Name is changed, new name:', name);
}, [name]);
useEffect(() => {
// shows message if any of scope values is changed
console.log('Some values of user scope are changed', name, city, age);
}, [globalScope]);
return (
<div>
User Name: {name} <br />
City: {city} <br />
Age: {age} <button onClick={increaseAge}>+</button>
</div>
);
}
3.1.3. Using Global Scope at Class Components
For using Global Scope with Class Component please wrap the Class Component at Functional Component and provide Global Scope like a property.
Example:
export default class UserClass extends React.Component<React.PropsWithChildren<UserProps>> {
increaseAge = () => {
if (this.props.userGlobalScope) {
const [ , setAge ] = this.props.userGlobalScope.age;
setAge((value: number) => value + 1);
}
}
render() {
if (!this.props.userGlobalScope) {
return (
<div>
User Scope is not provided
</div>
);
}
const [ name ] = this.props.userGlobalScope.name; // like useState
const [ city ] = this.props.userGlobalScope.city; // like useState
const [ age ] = this.props.userGlobalScope.age; // like useState
return (
<div>
Name: {name} <br />
City: {city} <br />
Age: {age} <button onClick={this.increaseAge}>+</button> <br />
</div>
);
}
}
const WrappedUserClass = ({ children = undefined }) => {
const userScope = useGlobalScope();
// you can use as many Global Scopes as you need.
return (
<UserClass
userGlobalScope={userScope}
>
{children}
</UserClass>
);
};
Or use HOC function withGlobalScope
that wraps your Class Component automatically for using Global Scope.
withGlobalScope(Component: React.Component, scopeToProp: Record<string, string> ) // Returns Higher-Order Component
Params:
- Component:
React.Component
- React.Component for wrapping at Global State. - scopeToProp:
Record<string, string>
- Object, where Key is a Global Scope name and Value is name of property that will be used to pass the Global State to the Component. It is allowed to use multiple scopes.
Returns:
- Returns Higher-Order Component
Examples:
export const UserClassWithGlobalScope = withGlobalScope(
UserClass, // Original Class, please see above
{ '': 'userGlobalScope' }, // <Global Scope Name for key>: <Class Property Name for value>
// Note, we use empty key for getting root scope to userGlobalScope prop
);
3.1.4. Creating Nested Global Scopes
createGlobalScope(scope: Scope) // Returns React.memo(React.FunctionalComponent)
Please note, the createGlobalScope
should be called once and outside a component implementation.
Params:
- scope:
Scope
- A Scope object. Allowed to have nested scopes.
Please note, for creating nested objects like a scope, the object should be wrapped by new Scope(...)
class.
Returns:
React.FunctionalComponent
(Wrapped by React.memo). It should be used inside your application (at any render level) for initialisation Global State feature for children components.
Examples:
import { Scope } from '@cheprasov/react-global-state';
const rootScope = new Scope({
app: new Scope({
settings: new Scope({
priceType: 'total',
}),
user: new Scope({
name: 'Alex',
city: 'London',
age: 37,
hobby: { // Not a scope
chess: 'beginner',
it: 'expert',
},
}),
search: new Scope({
departure: 'London',
destination: 'Paris',
date: Date.now(),
rooms: [
{ adult: 2 },
],
nights: 7,
filters: new Scope({
rating: 5,
price: {
min: 0,
max: 1000,
},
}),
}),
}),
});
const GlobalScope = createGlobalScope(rootScope);
root.render(
<GlobalScope>
<App />
</GlobalScope>
);
Use:
import { useGlobalScope } from '@cheprasov/react-global-state';
const App: React.FC = () => {
const app = useGlobalScope('app');
const [ departure, setDeparture ] = app.search.departure; // like useState
// if you need only nested scope like filters
const filters = useGlobalScope('filters');
const [ rating, setRating ] = filters.rating; // like useState
useEffect(() => {
console.log('Any property of the app scope or any nested scope is updated');
}, [app]);
useEffect(() => {
console.log('Any property of the app.search scope or any nested scope if app.search is updated');
}, [app.search]);
useEffect(() => {
console.log('Any property of the app.search.filters scope is updated');
}, [app.search.filter]);
useEffect(() => {
console.log('Rating property of the app.search.filters scope is updated');
}, [rating]); // or `app.search.filter.rating.stateValue`
// Note `app.search.filter.rating.stateValue` is an alias for app.search.filter.rating[0];
return (
<div className="App">
<SomeComponents>
...
</SomeComponents>
...
</div>
);
}
3.1.5. Import/Export Global Scopes
useGlobalScope(...)
returns an instance of ScopeVariablesWrapper
object. The objects has the following methods:
toObject()
- it converts states of scope and nested scopes to JavaScript object. The object could be used any way you want.
Examples:
import { Scope } from '@cheprasov/react-global-state';
const rootScope = new Scope({
app: new Scope({
settings: new Scope({
priceType: 'total',
}),
user: new Scope({
name: 'Alex',
city: 'London',
age: 37,
hobby: { // Not a scope
chess: 'beginner',
it: 'expert',
},
}),
search: new Scope({
departure: 'London',
destination: 'Paris',
date: Date.now(),
rooms: [
{ adult: 2 },
],
nights: 7,
filters: new Scope({
rating: 5,
price: {
min: 0,
max: 1000,
},
}),
}),
}),
});
const GlobalScope = createGlobalScope(rootScope);
root.render(
<GlobalScope>
<App />
</GlobalScope>
);
Use:
import { useGlobalScope } from '@cheprasov/react-global-state';
const App: React.FC = () => {
const app = useGlobalScope('app');
const [ departure, setDeparture ] = app.search.departure; // like useState
// if you need only nested scope like filters
const filters = useGlobalScope('app.search.filters');
const [ rating, setRating ] = filters.rating; // like useState
useEffect(() => {
console.log('Any property of the app scope or any nested scope is updated');
}, [app]);
useEffect(() => {
console.log('Any property of the app.search scope or any nested scope if app.search is updated');
}, [app.search]);
useEffect(() => {
console.log('Any property of the app.search.filters scope is updated');
}, [app.search.filter]);
useEffect(() => {
console.log('Rating property of the app.search.filters scope is updated');
}, [rating]); // or `app.search.filter.rating.stateValue`
// Note `app.search.filter.rating.stateValue` is an alias for app.search.filter.rating[0];
return (
<div className="App">
<SomeComponents>
...
</SomeComponents>
...
</div>
);
}
Something does not work
Feel free to fork project, fix bugs, write tests and finally request for pull