@rootzjs/core
v2.2.0
Published
A Modern state management tool for react application
Downloads
19
Maintainers
Readme
Rootz JS, is an open source JavaScript Library for maintaining application state.
It makes it easy to manage the state of your application. Another way of looking at this is, it helps you manage the data you display and how you respond to user actions. It helps you to view your application state as a whole at any point during application run.
Rootz, helps you write applications with complete hold over its state. This makes it possible for you to monitor changes happening at each component level.
Just think of it as a root of a plant, which starts as a single node (index.js). As you go deeper it spreads into interconnected bunch of nodes. A way to visualize your applications.
Note: Presently Rootz JS is made with React in mind. We are planning to extend support to other JavaScript Libraries & Frameworks.
Rootz Devtools
Rootz Devtools provides a incomparable coding and debugging experience. It provides see through visualization to the application's Root.
For more info, visit devtools.rootzjs.org (currently in development with additional features).
Getting to know more…
To implement Rootz, basic understanding of JavaScript would do just fine.
There are four key elements which one needs to be aware of for implementing Rootz.
Node
- A Node represents a React Component.
- All Nodes have a unique Id, called as NODE_ID.
- Each Node has it's own state which internally is a part of the Application Root.
- A React-Rootz application depicts a Root (A Tree in DS terms) with interconnected Nodes.
- An instance of Node provides helper methods to create Actions and Contracts.
Root
- A Root is basically a tree (in DS terms), which is defined with the instances of all the Nodes in an Application.
- It is true to the application's current state, hence the only source of truth.
Actions
- Actions are functions which updates the state of the Node in which it is defined.
- An Action should always return a JavaScript Object.
- Actions are limited to the scope of the Node it is defined in.
Contract
- Contract is an agreement made to update the state of any Node in the Application, accept the one in which it is defined (for which we have Actions).
- Contract is an Action by nature, with an only difference; that it updates the state of other Nodes.
Installation
Official Rootz JS template for Create React App
npx create-react-app my-app --template rootzjs
To install the core library with npm:
npm install —-save @rootzjs/core
with yarn:
yarn add --dev @rootzjs/core
Once all the dependencies are installed, we are all set to use Rootz.
Getting Started...
Now as you are all ready to use Rootz, lets go through the implementation of the concepts we know so far.
Node
A Node represents a stateful component in your Rootz application. You can create a Node with a createNode hook which Rootz provides. The createNode hook accepts a unique Id NODE_ID of type string and a Component (Functional or Class).
In return it provides an array of 2, first an Object node while the later being a HOF (High Order Function) dispatchNode.
Note: The node object has few helper methods for creating Actions and Contract (discussed in later examples). It holds Id, Component, State and other details of the Node.
Calling the dispatchNode function returns a Component (Which basically is the passed Component with additional features).
import { createNode } from '@rootzjs/core';
/*
* @param-01: NODE_ID
* @type: string
*
* @param-02: Component
* @type: {React.FunctionalComponent} / {React.ClassComponent}
*
* @Component-arguments: { props, state, actions, profile }
* @type: NodeProps
* @proptype: Object
*/
const [node, dispatchNode] = createNode("MyFirstNode", NodeProps => {
return (
{YOUR JSX ELEMENTS GOES HERE}
)
});
// MyFirstNode is a Component
export const MyFirstNode = dispatchNode(node);
Tips: Keeping the name of NODE_ID and Component same helps in better Identification.
NodeProps
NodeProps is an Object passed as an argument to the Component defined in a Node. It can be destructured to props, state, actions, profile.
props
Parameters which are passed by the parent (Caller) Component / Node.
state
The state object which you have defined using node.state method.
actions
Actions can be created using methods defined in node object. Same goes with contract. (explained in later sections).
profile
Profile are simply an utilization of the concept of Context in React. It helps in storing the most generic information.
Note: We will be discussing about state, actions and profile in details ahead.
State
A State is a simple JavaScript Object. You need to define the state to access it inside the Component. The method state takes an Object as a parameter.
import { createNode } from '@rootzjs/core';
const [node, dispatchNode] = createNode("MyFirstNode", ({
state,
}) => {
return (
<p>{state.message}</p>
)
});
// Defining the State of a Node
node.state({
message: "Yay! I just created my first Node State"
});
export const MyFirstNode = dispatchNode(node);
Actions
Actions are functions which returns Objects. The returned object signifies a newer state which will be merged to the existing state of the application.
Node provides methods to create Actions useAction, useActionCallback, useActionWith
useAction
This takes ACTION_ID and an Object or function as a parameter.
A Pure Action
In case the parameter is passed as an Object, Rootz creates a function with the name ACTION_ID. This function can then be accessed within the Component with property actions of NodeProps. A Pure Action.
A Callback Action
If a function is passed as a parameter, the same can then be accessed within the Component by property actions of NodeProps with the name defined in ACTION_ID. The callback provides the state of the Node as the first argument, followed by the array of props passed.
import { createNode } from '@rootzjs/core';
const [node, dispatchNode] = createNode("MyFirstNode", ({
state,
actions,
}) => {
return (
<React.Fragment>
{
state.message ?
<p>{state.message}</p>
:
<p>No Message to display</p>
}
<button onClick={actions.ADD_MESSAGE}>Add Message</button>
</React.Fragment>
)
});
node.state({
message: null
});
// create action for updating the state on btn click
node.useAction(
"ADD_MESSAGE",
{
message: "Yay! I just created my first Action"
}
);
export const MyFirstNode = dispatchNode(node);
useActionCallback
This is similar to useAction method with function as a parameter. A Callback Action.
import { createNode } from '@rootzjs/core';
const [node, dispatchNode] = createNode("MyFirstNode", ({
state,
actions,
}) => {
return (
<React.Fragment>
{
state.message ?
<p>{state.message}</p>
:
<p>No Message to display</p>
}
<button onClick={() => actions.ADD_MESSAGE("Action", "Callback")}>Add Message</button>
</React.Fragment>
)
});
node.state({
message: null
});
node.useActionCallback(
"ADD_MESSAGE",
(state, [text1, text2]) => ({
message: `Yay! I just created my first ${text1} ${text2}`
})
);
export const MyFirstNode = dispatchNode(node);
The state represents the current state of the Node MyFirstNode. followed by the props passed.
Contract
An Action updates the state of the node within its scope. What if you want to update any other Node, which is currently not within the scope in which the action is defined. Here comes the concept of a Contract.
A Contract is an agreement made with an Action to update a particular Node.
Node provides methods to create Contract useContract and useContractCallback.
useContract
Is a pure Action which takes 3 parameters, forNode which states which Node to be updated, ACTION_ID and an Object or a function.
useContractCallback
similarly useContractCallback takes forNode and a function as a parameter. The callback provides the state of the target Node as the first argument, followed by the array of props passed.
NodeA.js
import { createNode } from '@rootzjs/core';
const [node, dispatchNode] = createNode("NodeA", ({
state
}) => {
if(state.message) {
return (
<p>{state.message}</p>
)
} else {
return (
<p>No Message to display</p>
)
}
});
node.state({
message: null
});
export const NodeA = dispatchNode(node);
In the above example I have created a Node as NodeA which has a state variable message defined as null. NodeA would output No Message to display. This is understood.
Let's create a Node as NodeB which would have a Contract made with NodeA to update the message variable.
NodeB.js (with useContract)
import { createNode } from '@rootzjs/core';
const [node, dispatchNode] = createNode("NodeB", ({
actions
}) => {
return (
<button onClick={actions.ADD_MESSAGE}>Add Message</button>
)
});
node.useContract(
"NodeA",
"ADD_MESSAGE",
{
message: "Yay! I just created my first Contract"
}
);
export const NodeB = dispatchNode(node);
Now when the user clicks on the Add Message button, action ADD_MESSAGE will be called. The existing state of NodeA will be updated with the new state. Thus printing the message "Yay! I just created my first Contract".
The same goes for the useContractCallback.
NodeB.js (with useContractCallback)
import { createNode } from '@rootzjs/core';
const [node, dispatchNode] = createNode("NodeB", ({
actions
}) => {
return (
<button onClick={() => actions.ADD_MESSAGE("Contract", "Callback")}>Add Message</button>
)
});
node.useContractCallback(
"NodeA",
"ADD_MESSAGE",
(state, [text1, text2]) => ({
message: `Yay! I just created my first ${text1} ${text2}`
})
);
export const NodeB = dispatchNode(node);
In the above example NodeB.js (with useContractCallback) the first argument state will represent the current state of NodeA.js, followed by the list of parameters passed by the action ADD_MESSAGE.
Profile
Profile is similar to the concept of Bus in Networks. It helps you to store application specific generic data which could be accessible at any point within the application execution.
You can add data to profile by setProfile(Object) method. setProfile takes an Object as a parameter. Profiles can be set through various means.
import { createNode } from '@rootzjs/core';
const [node, dispatchNode] = createNode("MyFirstNode", ({
profile
}) => {
return (
{YOUR JSX ELEMENTS GOES HERE}
)
});
node.setProfile({
sessionId: "9694dca333f01433ecb179d32251c946"
});
export const MyFirstNode = dispatchNode(node);
Note: The profile if set through the node method (node.setProfile) can be accessed throughout the component irrespective to where it has been set. While in case of setProfile method imported as mentioned below should be set before the other components tries to fetch it.
import { setProfile } from '@rootzjs/core';
...
// storing a sessionId in the profile
setProfile({
sessionId: "9694dca333f01433ecb179d32251c946"
});
Within a Node you can access the application Profile from profile property of NodeProps.
Note: You can only access data from the profile if it is added before it is being fetched. Try adding profiles in the higher hierarchy Nodes.
import { createNode } from '@rootzjs/core';
const [node, dispatchNode] = createNode("MyFirstNode", ({
profile, // Application Profile can be accessed here
}) => {
return (
<p>{state.message}</p>
)
});
Outside a Node you can access them from the getProfile() method.
import { getProfile } from '@rootzjs/core';
...
// accessing profile
const sessionId = getProfile()["sessionId "];
Note: Hey, We are still working on the documentations and few examples for better understanding of its usage.
Thanks
To all those who have inspired me to come up with a new JavaScript state management tool.
Key Inspiration
I came across a very famous Restuarant, visited by more than 4.8 Million every week. When I personally visited that place I found as if I had to cook my own food, with so many procedures to be followed, even for a simple task of drinking water ;)
This struck me, and became an inspiration for Rootz JS.
But Why Rootz?
We donot expect you to cook when you visit our Restuarant. Just let us know what will you have and we would handle the rest. FYI feel free to drink as much water you want. You donot have to even mention about it. :D
License
ISC