(with 100% code coverageπππΏππ½ππ»)
ReduxX is a lightweight yet super-powerful, very easy to learn, and very easy to set up React state management library.
- Step 1: Install ReduxX and Set Your Initial Global State
- Step 2: Set up ReduxX
- Step 3: Easily Get and Set Values to the Global State
- Alternate Input Formats for the ReduxX setState and getState Functions
- Obscure Your State Keys
- Object Form for Initial State
- Old Fashioned State Management
- Optimize Your ReduxX App
- Examples of ReduxX Usage
In larger React apps, it's really nice to have global state (global as in globally accessible, NOT stored as a global browser variable). But when normally dealing with a global state object, you may encounter a problem, your state may look something like this:
...
accountSettingsEmailVerified: true
user: 'dandan69'
userProfileFirstName: 'Danny'
userProfileMainPicture: null
userProfileMainPictureIsPublic: true
...
And when getting and setting the global state, it looks something like this:
// the old way: getting value from the global state
const mainPicture = (
globalStateStorageInstance.state.userProfileMainPicture
);
// the old way: setting a value in the global state
globalStateStorageInstance.setState({
userProfileMainPicture: 'https://image.png'
});
Alternatively, you can use nested React state to have nicer variable names to deal with. The problem with that is it gets unnecessarily confusing and messy looking:
/*
the old way: updating a nested component
in this example, assume the state key "pictures"
has an associated value which is an
object with several properties
Below is how you would update
a single property of the pictures object
while preserving the other properties
*/
globalStateStorageInstance.setState( previousState => {
return {
pictures: Object.assign(
{},
previousState.pictures,
{
mainPicture: 'https://image.png'
}
)
};
});
πEwww, that's not very nice.π°
with ReduxX, the same state as above is automatically generated and it will look something like this:
...
accountSettings-EmailVerified: true
user: 'dandan69'
user-profile-firstName: 'Danny'
user-profile-mainPicture: null
user-profile-mainPicture-isPublic: true
...
and to get and set the state, you just need to do this:
// cleanly get a global state value with ReduxX:
const mainPicture = reduxX.getState( 'user', 'profile', 'mainPicture' );
// smoothly set a global state value with ReduxX:
reduxX.setState( [ 'user', 'profile', 'mainPicture' ], 'https://image.png' );
Notes:
a) with ReduxX, you can use any number of keys
b) Check out the Alternate Input Formats for the ReduxX setState and getState Functions section for other ways to use the
getState
andsetState
functions
ReduxX is incredibly simple to install and learn. Everything you need to know is contained in this README.md file. For ReduxX you only need to follow the 3 simple steps in the How ReduxX Works Section below! Essentially, you only need to be concerned with two functions: reduxX.getState
, and reduxX.setState
. These functions work exactly like normal React state, except you can use them anywhere in your code to access a global state, wow!
To install ReduxX, input the following npm command:
npm install reduxx --save
In the directory of your main React component, the most parent component that contains all your other components, add the following reduxx.js
file:
'use strict';
const ReduxX = require( 'reduxx' );
module.exports = ReduxX({
initialState: [
{
keys: [ 'monkey', 'favoriteFood' ],
value: 'banana',
},
{
// can put a string for the "keys" for single key values
keys: 'monkey',
// the value can be of any type,
// just like using regular React state
value: { name: 'Curious George', bff: 'Donkey Kong' },
},
{
keys: [ 'monkey', 'height' ],
value: '69cm',
},
{
keys: [ 'hippo', 'status', 'mood' ],
value: 'hungry',
}
]
});
Notes:
a) you can use any number of keys
b) technically you can put this reduxx.js file anywhere, but it makes the most sense to put it in your root folder based on how you access it (in Step 3)
c) you can alternatively use the Object Form for Initial State
In the most parent component itself, the component that contains all your other components, set up the ReduxX global state like the following:
'use strict';
const React = require( 'react' );
// Step 2: a) Import the following setupReduxX function
// from the file you created in Step 1.
// Note: this particular path assumes the reduxx.js file
// is in the same directory as this file
const { setupReduxX } = require( './reduxx.js' );
// Your "most parent" component
module.exports = class App extends React.Component {
constructor( props ) {
super( props );
// Step 2: b) Add the setupReduxX function here,
// pass in "this" as the first argument
setupReduxX( this );
}
render() {
...
}
}
Now anywhere you normally do a React setState, you can now setState with ReduxX to access a global state and never have to worry about collisions, so exciting!:
'use strict';
// some other module
const React = require( 'react' );
const reduxX = require( /*path to reduxx.js file, the file created in Step 1*/ );
function handleClick() {
/*
ReduxX Effortless State Setting and Getting
*/
// set the global state for one or more items like this:
reduxX.setState(
{
keys: [ 'monkey', 'favoriteFood' ],
value: 'apple',
},
{
keys: [ 'hippo', 'status', 'mood' ],
value: 'full'
}
);
// get the global state for an item like this:
const monkeyHeight = reduxX.getState( 'monkey', 'height' );
console.log(
`The reduxX monkey is ${ monkeyHeight } tall!`
);
// should log: The reduxX monkey is 69cm tall!
}
module.exports = class SomeDiv extends React.Component {
render() {
return <div onClick={ handleClick } />;
}
}
This example also includes a reduxX.getState
invocation. You can use this function anywhere and this function gets the requested value from the global state! Extreme #swaggyπΈπ
πΏπ³π½π!
Wow that's it, so easy!
All you need to do is require your ./reduxx.js
file you created in Step 1 and then use reduxX.setState
and reduxX.getState
to manage your global state (like in Step 3).
For your convenience, and for better code readability, the ReduxX setState
and getState
functions offer several ways to set and get values to and from the global state.
'use strict';
const {
getState,
setState,
} = require( /*path to reduxx.js file, the file created in Step 1*/ );
/*
Different input formats for reduxX.getState
*/
// The following four getState invocations are equivalent:
getState( 'user' );
getState( [ 'user' ] );
getState({ keys: 'user' });
getState({ keys: [ 'user' ] });
// The following three getState invocations are equivalent:
getState( 'user', 'profile' );
getState( [ 'user', 'profile' ] );
getState({ keys: [ 'user', 'profile' ] });
/*
Different input formats for reduxX.setState
*/
// The following four setState invocations are equivalent:
setState( 'user', { id: 69 } );
setState( [ 'user' ], { id: 69 } );
setState({ keys: 'user', value: { id: 69 } });
setState({ keys [ 'user' ], value: { id: 69 } });
// The following two setState invocations are equivalent:
// (these invocations involve setting multiple values at once)
setState(
[ 'user', 'name' ], 'Danny',
'user', { id: 69 },
[ 'user', 'game' ], 'React state library author'
);
setState(
{
keys: [ 'user', 'name' ],
value: 'Danny'
},
{
keys: [ 'user' ],
value: { id: 69 }
},
{
keys: [ 'user', 'game' ],
value: 'React state library author'
}
);
Recalling our state from before:
...
accountSettings-EmailVerified: true
user: 'dandan69'
user-profile-firstName: 'Danny'
user-profile-mainPicture: null
user-profile-mainPicture-isPublic: true
...
You can obscure your state keys by automatically turning them into GUIDs so that your state will look something like this:
...
206ca40e-e643-17b6-da3c-616b90800f4e: true
335a8849-9bff-bd2c-9a1a-6f26d61e0f88: 'dandan69'
ae771d1a-f563-c876-3132-b958e62ca205: 'Danny'
1721716f-4d92-ea42-56ca-5df95c175413: null
e04659d7-38a6-bae8-b513-36cfcb300ba7: true
...
To obscure your keys, simply set up your ReduxX (Step 1), like this:
'use strict';
const ReduxX = require( 'reduxx' );
module.exports = ReduxX({
// adding this will obscure your state keys
obscureStateKeys: true,
initialState: [
... // the same initial state objects go here
],
});
In Step 1, we set up our initial state with an array of
objects that each have a keys
and a value
property. We can alternatively set up our initial state with an object.
This can be more suitable for ReduxX apps with a larger number of state keys because it easily allows you to organize your initial state in a modular way (it easily allows you to put sections of your initial state in different files if you want).
The below code shows you how to use the Object Form for Initial State to set the same initial state as in Step 1.
'use strict';
const ReduxX = require( 'reduxx' );
// use the ReduxX VALUE key to set the initial state value for the specified key
const { VALUE } = ReduxX;
module.exports = ReduxX({
initialState: {
monkey: {
[VALUE]: { name: 'Curious George', bff: 'Donkey Kong' },
height: {
[VALUE]: 'banana'
},
height: {
[VALUE]: '69cm'
}
},
hippo: {
status: {
mood: {
[VALUE]: 'hungry'
}
}
}
}
});
You can also access and alter the global state manually. Use reduxX.getStore()
to get the global state storage instance (the store) with its state. The store is just a normal React Component instance that has a normal React state.
'use strict';
// some other module
const React = require( 'react' );
const reduxX = require( /*path to reduxx.js file, the file created in Step 1*/ );
function handleClick() {
/*
Old Fashioned State Setting and Getting:
*/
const store = reduxX.getStore();
// You can get and set regular React state keys
// in the global state storage component instance:
store.setState({ hello: 'world' });
setTimeout( () => {
const world = store.state.hello;
console.log( 'hello:', world, 'πππΌπ½π²π' );
// should log: hello: world πππΌπ½π²π
}, 0 );
/*
Using the stateKeyMapper and the KEY,
you can work with regular ReduxX type keys
OR obscured state keys.
See the "Obscure Your State Keys" section above
for more information about that topic.
*/
const { stateKeyMapper, KEY } = reduxX;
// setting the state:
// this will produce the same state change as in Step 3
const newState = {};
const monkeyFavFoodKey = stateKeyMapper.monkey.favoriteFood[ KEY ];
newState[ monkeyFavFoodKey ] = 'apple';
const hippoMoodKey = stateKeyMapper.hippo.status.mood[ KEY ];
newState[ hippoMoodKey ] = 'full';
store.setState( newState );
// getting the state:
// once again it's the same as getting the state like in Step 3
const monkeyHeightKey = stateKeyMapper.monkey.height[ KEY ];
const monkeyHeight = store.state[ monkeyHeightKey ];
console.log( `The reduxX monkey is ${ monkeyHeight } tall!` );
// should log: The reduxX monkey is 69cm tall!
}
One of ReduxX's principles is to be very easy to learn and to work in a similar way to React's regular state. As a result, optimizing your web app that uses ReduxX is similar to optimizing any other React app.
One thing to note is having a global state in the most parent component means that all the children of that parent component can re-render upon any state change. This re-rendering occurs even if those children don't need to be changed themselves due to a state change (the component will render the same element but it won't change the DOM because the newly rendered element was the same as the last). This results in "wasted" renders. As noted in the Official React Optimization Documentation, these "wasted" renders in many cases don't affect performance.
In the case where you would like to optimize your ReduxX app, here is a way to optimize your components to avoid unnecessary re-rendering:
Suppose you have a "p" (html paragraph) based component that displays text. Next suppose that text is based on the global state value whose key is pText
and you only want that "p" component to re-render when pText
changes.
First of all, here is what our original unoptimized "p" component looks like, let's call it the MegaPComponent
:
'use strict';
const React = require( 'react' );
const { getStore } = require( /* path to reduxx.js file created in Step 1 */ );
module.exports = class MegaPComponent extends React.Component {
render() {
const store = getStore();
const { pText } = store.state;
return <p> { pText } </p>;
}
}
Here is what you do, put that "p" component in a div container, lets call that container Container
:
'use strict';
const React = require( 'react' );
const MegaPComponent = require( '...path to MegaPComponent' );
const { getStore } = require( /* path to reduxx.js file created in Step 1 */ );
module.exports = class Container extends React.Component {
render() {
const store = getStore();
const { backgroundColor } = store.state;
const divStyle = { backgroundColor };
return(
<div style={ divStyle }>
<MegaPComponent/>
</div>
);
}
}
This is still unoptimized, the MegaPComponent
will do a no-op render (a "wasted" render) if the backgroundColor
state value or any other global state value changes.
To avoid this, what you need to do is set the pText
as a prop of the MegaPComponent
, and extend the MegaPComponent
from the React.PureComponent
class like this:
'use strict';
const React = require( 'react' );
module.exports = class MegaPComponent extends React.PureComponent {
render() {
const { pText } = this.props;
return <p> { pText } </p>;
}
}
and change your Container
to be like this:
'use strict';
const React = require( 'react' );
const MegaPComponent = require( '...path to MegaPComponent' );
const { getStore } = require( /* path to reduxx.js file created in Step 1 */ );
module.exports = class Container extends React.Component {
render() {
const store = getStore();
const { backgroundColor, pText } = store.state;
const divStyle = { backgroundColor };
return(
<div style={ divStyle }>
<MegaPComponent pText={ pText }/>
</div>
);
}
}
In the MegaPComponent
, by setting the pText
value as a prop, and by making the MegaPComponent
extend from the React.PureComponent
class, it will now only do a render when the pText
value changes. You can apply this optimization technique to any component where you want to avoid "wasted" renders. In some cases, you may need to adjust the shouldComponentUpdate
React component method in order to achieve the same effect because React PureComponents only do a shallow comparison of the previous and next props and state. Here is a more detailed explanation of how this works from the Official React Documentation on Pure Components.
-
NeverCodeAlone.ca: a sample website using ReduxX (GitHub repo with code)
-
Easy React App: a base website framework made with React, React Router, Webpack, and ReduxX.
-
LessonShop.net: a lessons marketplace built in React that uses ReduxX for state management.
Check out LessonShop.net to take or teach coding lessons! Promote your personal brand as a developer or as a developer teacher for free!