Manage local and global states with one hook.
Group States, Reducers, Selectors and Callbacks in a Struct (like a Class) and keep your code organized.
Works globaly too (like Redux).
------ Main features: -------
- Simple and intuitive syntax (val, get, set, act).
- Easy persistence.
- Parent and children logic.
- Extension and inheritance.
- Context API.
- Natural source code for JS and React (made with functions and Hooks).
- Free of useEffects (side effects doesn't work in SSR).
------ Disclaimer: -----
- No TypeScript yet (sorry).
- useStruct is essentially a hook, so it will not work in Next's getInitialProps.
- Debounce and throttle effects doesn't work in selectors and callbacks too.
- In Redux a action can dispatch many others actions and modify the state multiple times sequentially. But this is impossible in useStruct. By definition a hook can modify the state only one time when called. Keep it in mind.
Overview:
// yarn add use-struct immer -D const Calculator =
In component:
{ // like a class const Calculator = return <div> /* ======== STATE ======== */ <h1>Greetings: Calculatorgreetings</h1> <h2>Current counter: Calculatorcounter</h2> <br /> /* ======== SELECTORS ======== */ <h3>Added counter by 3: Calculatorget</h3><br /> <h3>Multiplied counter by 2: Calculatorget</h3><br /> <br /> /* ======== REDUCER ======== */ <button onClick= Calculatorset >Increment</button><br /> <button onClick= Calculatorset >Decrement</button><br /> <br /> /* ======== CALLBACKS ======== */ <button onClick= Calculator >Add counter by 4</button><br /> <button onClick= Calculator >Sub counter by 2</button><br /> </div> }
Examples:
1) Fetching data from an API
// yarn add use-struct immer axios react-json-pretty -D { // ================ structs ================ // const ForeignData = // ================ render ================ // return <div> /* ======== STATE ======== */ <h1>Description: ForeignDatadescription</h1> <RenderJson data=ForeignDataval /> /* ======== CALLBACKS ======== */ <button onClick= ForeignData >Fetch data</button> </div> }
use
for hooks and efc
for effects. **
2) ** New recent features: Such that constructor of a Class, the useStruct can invoke initial hooks too and provide them by prefix .use
.
And even more, finally you can put side effects (such that useEffect) for react to changes of state.
Example:
{ // ================ structs ================ // const Calculator = // ================ render ================ // return <div> /* ======== HOOKS ======== */ <h1>Counter: Calculatorcounter</h1> /* ======== CALLBACKS ======== */ <button onClick= Calculator >increment</button> </div> }
./models
and ./pages
folders in the source code.
3) Consult more examples in Clone the repository and test it immediately with yarn dev
in http://localhost:3000
.
Bonus features:
1) Global structs
The library provides two extras resources:
<ScaffoldProvider />
component.- And
useScaffold()
hook.
"Scaffold" is just a alias for "Global Struct". These resoucers are a implementation of the <Context.Provider />
and useContext()
.
The value
provided by <ScaffoldProvider />
is a root instance of the useStruct()
hook. This instance can be recovered by any component in the application using useScaffold()
.
Implements it in 3 steps:
First, in the ./store
folder, create a function that return a instance of useStruct()
hook.
// the root struct { return }
Second. Wrap your application with the <ScaffoldProvider struct={struct} />
passing the function above for struct
attribute: See:
// the root struct { return <ScaffoldProvider struct=struct > <RootApp /> </ScaffoldProvider> }
Third. Recovery the useStruct()
instance in some component with the useScaffold()
.
{ // the root struct const Struct = return <div> <h1>Greetings: Structgreetings</h1> </div> }
2) Memorizing and updating Selectors and Callbacks
Such that useMemo
and useCallback
, selectors get
and callbacks act
can be memorized too. Just add an array of dependencies after definitions.
const Calculator =
If none dependencies array was added all selectors and callbacks will react at each change of state.
3) Wrapping others structs
A struct can wrap another and all properties and methods of the wrapred structs will are avaliable inside and outside for the wraper struct. See:
const AdditionModule =
const SubtractionModule =
Wrapped structs inside of the wrapper
You can access the wrapped structs by prefix .str
.
str.AdditionModule.blabla
str.SubtractionModule.blabla
const Calculator =
Wrapped structs outside of the wrapper:
You can access the wrapped structs like as child property.
Calculator.AdditionModule.blabla
Calculator.SubtractionModule.blabla
{ const SubtractionModule = const AdditionModule = const Calculator = return <div> /* ======== STATES ======== */ <h1>Description: Calculatordescription</h1> <h3>Addition counter: CalculatorAdditionModulecounter</h3> <h3>Subtraction counter: CalculatorSubtractionModulecounter</h3> /* ======== SELECTORS ======== */ <h3>Addition counter: Calculatorget</h3> <h3>Subtraction counter: Calculatorget</h3> /* ======== CALLBACKS ======== */ <button onClick= CalculatorAdditionModuleset >Increment</button> <button onClick= CalculatorSubtractionModuleset >Decrement</button> <button onClick= Calculator > Increment and decrement all counters </button> </div> }
str, val, get, set, act
are as a alias for this
4) In a Class
others properties and methods can be accessed by prefix this
. Similarly in a struct sibling properties and methods can be accessed by prefixes str, val, get, set, act
.
See this full example:
const Struct =
5) Persistence
The state of each useStruct
can be easy persisted in local storage
adding a key: 'STRUCT_NAME'
and pst: true
in the hook input. See:
// persisted structconst Struct =
6) Extending others structs
Similarly to a Class
a struct can extend many others structs and all their properties and methods will are DIRECTLY available for the extended struct WITHOUT PREFIX .str
.
const AdditionModule = const SubtractionModule =
Accessing the extended features by inside
const Calculator =
Accessing the extended features by outside
{ // ================ structs ================ // const SubtractionModule = const AdditionModule = const Calculator = // ================ render ================ // return <div> /* ======== STATES ======== */ <h1>Addition Module Counter: CalculatoraddCounter</h1> <h1>Subtraction Module Counter: CalculatorsubCounter</h1> <h2>Diff between subCounter and addCounter Calculatorget</h2> /* ======== CALLBACKS ======== */ <button onClick= Calculator > increment and decrement all counters </button> </div> }
As explained, set.increment()
, set.decrement()
, val.subCounter
and val.addCounter
are directly invoked by inside. And Calculator.addCounter
and Calculator.subCounter
are too directly invoked by outside.
In both cases looks that these props and methods was created by Calculator
struct, but actually, their was created by extended modules and inherited by Calculator struct.
ATTENTION:
** To extend a struct not means create a deepClone and modify their copy, but means TO CONTROL IT DIRECTLY. All change of state will put definitely in all original states, not in deepClones. **
** In extends operations occurs a merge of props and methods. So be careful for not create nothing with the same name. If a match occurs, the wraper struct will override the features of the extension structs . **
Details:
1) Shortcuts
- States
val
, Callbacksact
and Wrapped Structsstr
can be accessed directly by view without the prefixs.val
,.act
, and.str
. - But all selectors
get
and reducerset
needs be prefixed (Inside or outside of the hook).
Example with Hello World:
{ const Calculator = return <div> /* ======== STATE ======== */ <h1>: Calculatorgreetings</h1> <h1>: Calculatorvalgreetings</h1> /* ======== CALLBACKS ======== */ <button onClick= Calculator >Say </button><br /> <button onClick= Calculatoract >Say </button><br /> </div> }
2) Forbidden keywords
Don't create none props or methods with the following keywords: key
, pst
, ext
, str
, val
, get
, set
and act
. They are reserved keywords for this hook and if you do that, bad things can to happen.