React clean with the STOREs
Tags: react, javascript, pattern, store
Published: 2021-08-26 12:36:20

React is easy to use to create the "VIEW".
But when the application grows ... it's not enough!
Passing variables and methods in "props"
the code turns into leaves entangled in the nodes of the VIEW tree!

A practical example:

```jsx
import { useState } from "react"

// main with "data"
export default function App() {
  const [data, setData] = useState(0)
  return (
    <div className="App">
      <ShowDataCmp data={data} />
      <ContainerCmp data={data} onChange={setData} />
    </div>
  )
}

// render data
function ShowDataCmp({ data }) {
  const renderData = `Data: ${data}`
  return <div>{renderData}</div>
}

// simple container
function ContainerCmp({ data, onChange }) {
  return <div style={{ background: "blue", padding: "5px" }}>
    <ChangeDataCmp data={data} onChange={onChange} />
  </div>
}

// component for change data
function ChangeDataCmp({ data, onChange }) {
  const handleOnClick = (e) => {
    const newData = data + 1
    onChange(newData)
  }
  return <button onClick={handleOnClick}>Change Data</button>
}
```
[sandbox](https://codesandbox.io/s/exe-0-with-props-l5fjc)

![Frame 3](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/aev4our5m2zxg32vqr49.png)

Code and data are mixed in the VIEW.
If the application grows, you won't understand where the data and methods come from.
Let's face it: **it's real shit!**

---

## Context

The [Context](https://it.reactjs.org/docs/context.html) is React's "native" solution.

Reworking the previous example we get:

```jsx
import { createContext, useContext, useState } from "react"

const Context = createContext()

// main with "data"
export default function App() {
  const reducer = useState(0)
  return (
    <div className="App">
      <Context.Provider value={reducer}>
        <ShowDataCmp />
        <ContainerCmp />
      </Context.Provider>
    </div>
  )
}

// render data
function ShowDataCmp() {
  const reducer = useContext(Context)
  const renderData = `Data: ${reducer[0]}`
  return <div>{renderData}</div>
}

// simple container
function ContainerCmp() {
  return <div style={{ background: "blue", padding: "5px" }}>
    <ChangeDataCmp />
  </div>
}

// component for change data
function ChangeDataCmp() {
  const reducer = useContext(Context)
  const handleOnClick = (e) => {
    const newData = reducer[0] + 1
    reducer[1](newData)
  }
  return <button onClick={handleOnClick}>Change Data</button>
}
```

[sandbox](https://codesandbox.io/s/exe-1-with-context-5w0lb)

![Frame 4](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/lsyub63a8o2phpepuokk.png)

Not bad! But there are two problems:
- We have to create CONTEXT and STATE for each STOREs. If there were many STOREs the complexity would increase.
- It is not clear how to divide the BUSINESS LOGIC from the VIEW

---

## STOREs

There are tons of LIB out there!
If you want to stay light use [JON](https://github.com/priolo/jon)
it's just a little sugar on "Native Providers"
... and heavily influenced by [VUEX](https://vuex.vuejs.org/)
 
Our example could be:

```jsx
import { MultiStoreProvider, useStore } from "@priolo/jon"

const myStore = {
  // lo stato iniziale dello STORE
  state: {
    counter: 0
  },
  getters: {
    // 
    renderData: (state, _, store) => `Data: ${state.counter}`
  },
  actions: {
    increment: (state, step, store) => {
      store.setCounter(state.counter + step)
    }
  },
  mutators: {
    setCounter: (state, counter, store) => ({ counter })
  }
}

// main with "data"
export default function App() {
  return (
    <MultiStoreProvider setups={{ myStore }}>
      <div className="App">
        <ShowDataCmp />
        <ContainerCmp />
      </div>
    </MultiStoreProvider>
  )
}

// render data
function ShowDataCmp() {
  const { renderData } = useStore("myStore")
  return <div>{renderData()}</div>
}

// simple container
function ContainerCmp() {
  return (
    <div style={{ background: "blue", padding: "5px" }}>
      <ChangeDataCmp />
    </div>
  )
}

// component for change data
function ChangeDataCmp() {
  const { increment } = useStore("myStore")
  const handleOnClick = (e) => increment(1)
  return <button onClick={handleOnClick}>Change Data</button>
}
```

[sandbox](https://codesandbox.io/s/exe-2-with-jon-m1ffj)

![Frame 5](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/oqkjl8bmnuu1q1xco3cm.png)


## state 

*The initial STATE of the STORE. "Single Source of Truth"*
The STATE is connected to the VIEW (via React):
When the STATE changes then the VIEW updates automatically.
  
To access the STATE of a STORE:
```js
const { state } = useStore("MyStore")
```

Avoid conflicts:
```js
const { state:mystore1 } = useStore("MyStore1")
const { state:mystore2 } = useStore("MyStore2")
```

Outside the "React Hooks":
```js
const { state:mystore } = getStore("MyStore")
```

Then:
```html
<div>{mystore.value}</div>
```

---

## getters

*Returns a value of the STATE.*
Although you can access the STATE directly
in many cases you will want some processed data.

For example: a filtered list:  
```js
const myStore = {
   state: { 
	   users:[...] 
	   }, 
   getters: {
      getUsers: ( state, payload, store ) 
         => state.users.filter(user=>user.name.includes(payload)),
   }
}
```

```jsx
function MyComponent() {
   const { getUsers } = useStore("myStore")
   return getUsers("pi").map ( user => <div>{user.name}</div>)
}
```

The signature of a **getter** is:
- **state**: the current value of the STATE
- **payload**: (optional) the parameter passed to the getter when it is called
- **store**: the STORE object itself. You can use it as if it were "this"

> GETTERS should ONLY "contain" STATE and GETTERS

---

## mutators

*The only way to change the STATE.*
It accepts a parameter and returns the "part" of STORE to be modified.

For example:
```js
const myStore = {
   state: { 
	   value1: 10,
	   value2: "topolino",
	}, 
   mutators: {
      setValue1: ( state, value1, store ) => ({ value1 }),
	  // ! verbose !
	  setValue2: ( state, value, store ) => { 
		  const newValue = value.toUpperCase()
		  return {
			  value2: newValue
		  }
	  },
   }
}
```

```jsx
function MyComponent() {
    const { state, setValue1 } = useStore("myStore")
    return <button onClick={e=>setValue1(state.value1+1)}>
        value1: {state.value1}
    </button>
}
```

the signature of a **mutator** is:
- **state**: the current value of the STATE
- **payload**: (optional) the parameter passed to the mutator when it is called
- **store**: the STORE object itself. You can use it as if it were "this"

> Inside MUTATORS you should use ONLY the STATE.

---

## actions

*Contains the business logic*
ACTIONS can be connected to SERVICEs and APIs
They can call STATE values, MUTATORS and GETTERS
They can be connected to other STOREs
They can be async

A typical use:
```js
const myStore = {
    state: { 
	    value: null,
	}, 
    actions: {
        fetch: async ( state, _, store ) => {
            const { data } = await fetch ( "http://myapi.com" )
            store.setValue ( data )
        }
    },
    mutators: {
        setValue: ( state, value, store ) => ({ value }),
    }
}
```

```jsx
function MyComponent() {
    const { state, fetch } = useStore("myStore")
    return <button onClick={e=>fetch()}>
        value1: {state.value}
    </button>
}
```

the signature of a **action** is:
- **state**: the current value of the STATE
- **payload**: (optional) the parameter passed to the action when it is called
- **store**: the STORE object itself. You can use it as if it were "this"


## Conclusion

[JON](https://github.com/priolo/jon) is designed to be VERY LIGHT and integrated with React.
Basically it is a utility to use native [PROVIDERS](https://it.reactjs.org/docs/hooks-reference.html#usecontext)
You can easily see them in the browser tool
![screenshot1](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/wb7gb3lfgwlnlxduct3d.png)

Other link:
[sandbox](https://codesandbox.io/s/example-4-0jeqi?file=/index.js:106-124)
[template SPA](https://dev.to/priolo22/complete-template-for-react-spa-2021-2k5n)