tenx
A Flux implementation for javascript/React app
Installation
npm i tenx --save
Introduction
In this article, I am going to help you to learn the fundamentals of react and tenx. We will do this by implementing a todolist app using react and tenx.
Core concepts
Basically 2 core concepts in tenx:
- Store
- Actions
What is the store ?
The store is an object which holds all the app data. This holds the state of our app and this can be used everywhere in our project.
Unlikely redux, you can define many stores in your app, but we use only single store for todolist app
Actions
Actions are pure functions. You can only mutate app state inside the actions. So action is where you put all app logic.
An action takes two parameters. The first parameter is the store context. It contains many named state mutators and provides some util functions for advanced usages. The second parameter is an action payload.
Creating a simple todo app
Defining app state tree
Firstly, we need to define an app state tree. Todos state is an array of todo item, that looks like:
const state = todos: id: 1 title: "Todo 1" completed: false id: 2 title: "Todo 2" completed: false ;
Creating tenx store
We use tenx() function to create a store, the first parameter is initial state of store
import tenx from "tenx";const initial = // A store has one state, it names todos todos: id: 1 title: "Todo 1" completed: false id: 2 title: "Todo 2" completed: false ;const store = ;
Defining actions
We pass action map as second parameter of tenx() function
const store = ;
Let's create a first addTodo action
const store = ;
If you see the above code, we have created an action called ‘addTodo’. You can dispatch addTodo action by using store.actionName(payload):
// context object is passed automatically by store, we pass action payload onlystore;store;
Consuming store state
import React useRef from "react";import useStore from "tenx/react"; { const inputRef = ; const todos = ; { e; // dispatch addTodo action store; inputRefcurrentvalue = ""; } return <> <form => <input ="text" = ="What need to be done ?" /> </form> <ul> todos </ul> </> ;}
As you see above code, we import useStore() from "tenx/react". "tenx/react" is another entry point of tenx package. This entry includes many utils/hooks for React, we will discover them later on.
useStore() is a React hook, it takes 2 parameters. The first one is tenx store object, the second one is the state mapping function. The first parameter of the mapping function is the state object, it presents all state values of the store. In this case, we select todos value (its value is an array type, not state mutator) from the state object.
We successfully created addTodo action and consumed todos state. Now we need to create other actions, toggleTodo and removeTodo.
const store = ;
We need to update App component as well
return <> <ul> todos </ul> </>;
You can find full source here
Introducing computed state
In a previous section we have created some actions for todo app: addTodo, removeTodo, toggleTodo. Let's create component displays some useful info of todo list.
{ const allTodoCount activeTodoCount completedTodoCount = ; return <> <div></div> <div></div> <div></div> </> ;}
The TodoFilter component above needs 3 states: allTodoCount, activeTodoCount and completedTodoCount. Those states will be re-computed whenever the component renders, evenly no app state updated. By using derived/computed state, you can define some dynamic state computation easily, and computed states only re-compute when your app state changed. Let update the store creating code.
const store = ;
We need to refactor TodoFilter component to consume computed states
{ const allTodoCount activeTodoCount completedTodoCount = ; return <> <div></div> <div></div> <div></div> </> ;}
Let's go into deep dives on computed states. Computed state can be:
computedFunction(state)
A function that returns computed value
;
Tuple [...dependencyList, combiner]
Each dependency item can be:
- string (name of static state or other computed states)
- function (a function returns slice of state)
Combiner is a function that retrieves all values of dependency list and returns combined value.
;
How to persist app state
If you want to store your app state to persistence storage (sessionStorage, localStorage, AsyncStorage etc.). We can define init action to add some logics for handling app state changing.
const store = ;
An init action will be dispatched when store runs initializing phase. If you define init action is async function, your components cannot consume store state until init action finished.
const store = ;
In component render function, an error will be thrown if you try to consume in progress store. To handle store initializing progress, you must wrap your App component inside React.Suspense element.
import React Suspense from "react";import render from "react-dom";import App from "./compoments/App"; ;
You can find fully example about lazy state persistence here