Skip to content

Repository files navigation

React Simple State

A simple and predictable state management for React and React Native Applications.

Goals:

  • Easy to use and to maintain state management on a React Application.
  • Scalable
  • Easy to organize
  • Compatible with Hooks and Class based Components.

Inspired by:

Installation

  1. Run on your terminal the following command:
$ npm i --save @cobuildlab/react-simple-state
  1. To import the library anywhere you would like to use it:
import{createEvent,useSubscription,useEvent,View,}from'@cobuildlab/react-simple-state';

API Docsw

ObjectDescription
EventParamsParams for the createEvent function.
createEventHelper function to create state events.
ViewSubclass of React.View that includes a this.subscribe method to subscribe to changes on an Event.
useSubscriptionA hook for subscribe to specific events with a callback.
EventHookParamsParams for the useEvent
useEventA declarative alternative to the useSubscription.

EventParams

  • initialValue - An initial value for the event.
  • reducer A function that mutates the state before it gets propagated.

createEvent(params: EventParams)

  • Allows you to create a subscribable event.
  • The result object can be used for subscriptions with the View or the hooks: useEvent and useSubscription

Example

// agency-events.jsimport{createEvent}from'@cobuildlab/react-simple-state';exportconstOnAgencyList=createEvent();exportconstOnAgencyListError=createEvent();exportconstOnNewAgent=createEvent({initialValue: newAgent(),reducer: (prevState)=>{prevState.agencies=OnAgencyList.get();returnprevState;},});

View

  • Allows you to create subscription to events from Class based components
  • It handles the subscription lifecycle: subscribe and unsubscribe on react lifecycle events.

Example

// AgencyView.jsimport{View}from"@cobuildlab/react-simple-state";import{OnAgencyList,OnNewAgent}from"./agency-events.js"classAgencyViewextendView{componentDidMount(){this.subscribe(OnAgencyList,(state)=>{// So something with the state.})this.subscribe(OnNewAgent,(state)=>{// So something with the state.})}}

useSubscription(store, eventName, callback)

  • It subscribes a callback to an Event from functional components using hooks.
  • It handles the subscription lifecycle

Example

// AgencyView.jsimport{useSubscription}from"@cobuildlab/react-simple-state";import{OnAgencyList,OnNewAgent}from"./agency-events.js"constAgencyView=()=>{useSubscription(OnAgencyList,(state)=>{// Do something with the state});useSubscription(OnNewAgent,(state)=>{// Do something with the state});return();}

EventHookParams

  • initialValue - An initial value for the event.
  • reducer A function that mutates the state before it gets propagated.

useEvent(eventName, initialValue, reducer)

  • It subscribes ton event from functional components using hooks with a declarative approach.
  • It handles the subscription lifecycle
  • It handles initial values for the events
  • It handles a reducer function for the state.

Example

// AgencyView.jsimport{useEvent}from"@cobuildlab/react-simple-state";import{OnAgencyList,OnNewAgent}from"./agency-events.js"constAgencyView=()=>{conststate=useEvent(OnAgencyList);constagent=useEvent(OnNewAgent,{initialValue: {},reducer: (prevState)=>prevState.agent});return();}

Full Example

Let's build a Flux Workflow for authentication

1) First, declare your Events

import{createEvent}from'@cobuildlab/react-simple-state';exportconstLogoutEvent=createEvent({reducer: (prevState)=>{localStorage.clear();returnprevState;},});exportconstLoginEvent=createEvent();exportconstPermissionError=createEvent({reducer: (prevState)=>{LogoutEvent.dispatch();returnprevState;},});export{LogoutEvent,LoginEvent,PermissionError};

2) Registering with the Store changes

importReactfrom'react';import{LogoutEvent,LoginEvent,PermissionError}from"./agency-events.js";import{View,useSubscription,useEvent}from'@cobuildlab/react-simple-state';// Class BasedclassViewextendsView{constructor(){super();}componentDidMount(){this.subscribe(LoginEvent,(state)=>{// Do something useful with the Event DataconstuserName=state.user.name;this.setState({userName});});// Register some methodthis.subscribe(LogoutEventthis.logOutEvent);}logOutEvent=(state)=>{//DO something with the state or the state of the StoreconststoreState=LogoutEvent.get();}}// or Functional with React HooksconstView=(props)=>{// Set an Initial ValueconstloginState=useEvent(LoginEvent);constuserState=useEvent(LogoutEvent,{reducer:(state)=>state.user});useSubscription(LoginEvent,(state)=>{// setError// toast.error()});return({loginState&&<Useruser={loginState}>})};

3) Define some actions that will trigger the event

import{LoginEvent,LogoutEvent}from'./agency-events.js';constauthenticateAction=(username,password)=>{if(username===undefined)returnLogoutEvent.dispatch();letdataToSave={authenticated: true,
username,
password,};LoginEvent.dispatch(dataToSave);};export{authenticateAction};

4) Actions can be created with createAction helper

import{createAction}from'@cobuildlab/react-simple-state';import{OnFecthUserEvent,OnFetchUserErrorEvent}from'./events';import{apiClient}from'./api';// single declarition of the async service and the actionexportconstfetchUserAction=createAction(OnFecthUserEvent,OnFetchUserErrorEvent,async(id)=>{constuser=awaitapiClient.fetch({user: id});returnuser;},);// Or we could declare the async service and then use in with diferent actionsexportconstfetchUserService=async(id)=>{constuser=awaitapiClient.fetch({user: id});returnuser;};exportconstfetchMainUserAction=createAction(OnFecthMainUserEvent,OnFetchMainUserErrorEvent,fetchUserService,);exportconstfetchSecondaryUserAction=createAction(OnFecthSecondaryUserEvent,OnFetchSecondaryUserErrorEvent,fetchUserService,);

5) Fetch can be done with useFetchAction hook

import{useFetchAction}from'@cobuildlab/react-simple-state';import{fetchUser}from'./actions';// UserProfile componentexportconstUserProfile=({ userId, loadingToken })=>{const[user,loadingUser]=useFetchAction(fetchUser,[userId]);// OR... skip the fetch ultil the token loadsconst[user,loadingUser]=useFetchAction(fetchUser,[userId],{skip: loadingToken,});// OR...const[user,loadingUser]=useFetchAction(fetchUser,[userId],{onCompleted: ()=>{toast.success('user fetched');},onError: ()=>{toast.error('Error when fetching user');},});// OR...const[user,loadingUser,{ refetch }]=useFetchAction(fetchUser,[userId],{onCompleted: ()=>{toast.success('user fetched');},onError: ()=>{toast.error('Error when fetching user');},});useSubscription(OnSaveUserEvent,()=>{// refetch the user after saving form for examplerefetch();});return;// profile view};

6) callable fetch can be done with useCallAction hook

import{useCallAction}from'@cobuildlab/react-simple-state';import{saveUser}from'./actions';// UserProfile componentexportconstUserProfile=({ userId })=>{constuserData=userDataState;const[save,loadingSubmit]=useCallAction(saveUser);// OR...// setup the action, and return a function that will trigger the action when it needed.const[save,loadingSubmit]=useCallAction(saveUser,{onCompleted: ()=>{toast.success('user saved');},onError: ()=>{toast.error('Error when saving user');},});return(<Form><SubmitButtononClick={()=>save(userId,useCallAction)}/></Form>);};

Changelog

v0.6.0:

  • types improvements to be more genereic.

v0.5.0:

  • Add useCallAction and useFetchAction hooks to have a better declaritive way to handle promise in components.

v0.4.4:

  • Add isEmpty method to event to know if the event has data without to call the get method.

v0.4.0:

  • Add createAction decorator

v0.3.0:

  • Cache callback on useEvent
  • Cache the callback on useSubscription and add a dependencies parameter.
  • Remove Ramda and Rxjs as dependencies

v0.2.0:

  • Remove: receiveLastValue for the useQuery hook

v0.1.0:

  • Typos and documentation

v0.0.1:

  • State Draft

About

A simple and predictable state management for React and React Native Applications.

Resources

Stars

10 stars

Watchers

11 watching

Forks

Releases

Packages

Used by

Contributors

Languages