DEV Community

Fabio Nogueira
Fabio Nogueira

Posted on

React without Redux, simple store

Most applications do not need the complexity of Redux, in these simple cases I used the approach below and it is serving me well, let's go:

import React from 'react'
import ReactDOM from 'react-dom'

import { createStore, useStore } from './reactive'

createStore({
    count: 0
})

function MyComponent1(){
    const [ count ] = useStore('count')
    return (<h2>Component1: {count}</h2>)
}

function MyComponent2(){
    const [ count ] = useStore('count')
    return (<h2>Component2: {count}</h2>)
}

function App(){
    const [ count, setCount ] = useStore('count')

    function increment(){
        setCount(count + 1)
    }

    return (
        <>
            <h1>{count}</h1>
            <MyComponent1 />
            <MyComponent2 />
            <button onClick={increment}>increment</button>
        </>
    )
}

ReactDOM.render(
    <App />,
    document.getElementById('root')
);

Enter fullscreen mode Exit fullscreen mode

Yes, that's it!
Down with the lib reactive.js

import { useState, useEffect } from 'react'
import Dispatcher from './dispatcher'

let store = {}
const dispatcher = new Dispatcher()

function createStore(value) {
    store = value
}

function getStore(key) {
    return store[key]
}

function setStore(key, value) {
    store[key] = value
    dispatcher.emit('data', store)
}

function useStore(key) {
    const [value, setData] = useState(store[key])

    useEffect(() => {
        const fn = dispatcher.on('data', (data)=>{
            let value = data[key]

            if (Array.isArray(value)){
                setData(value)
            } else if (typeof(value) == 'object') {
                setData({...value})
            } else {
                setData(value)
            }
        })

        return ()=>{
            setImmediate(()=>{
                dispatcher.off('data', fn)
            })
        }
    })

    return [value, (v)=>{setStore(key, v)}]
}

export {
    getStore,
    useStore,
    setStore,
    createStore
}
Enter fullscreen mode Exit fullscreen mode

dispatcher.js

let immediateEvents = {}
let eventUID = 0

export default class Dispatcher {
    static registerImmediate(event) {
        immediateEvents[event] = true
    }

    constructor() {
        this.__callbacks__ = {}
        this.__uids__ = {}
    }

    on(event, callback, generateUID = false) {
        let uid

        if (!this.__callbacks__[event]) {
            this.__callbacks__[event] = []
        }

        this.__callbacks__[event].push(callback)

        if (immediateEvents[event]) {
            delete (immediateEvents[event])
            this.emit(event)
        }

        if (generateUID) {
            uid = `event${eventUID++}`
            this.__uids__[uid] = callback
        }

        return callback
    }

    off(event, callback) {
        let index
        let arr = this.__callbacks__[event] || []
        let fn = this.__uids__[callback]

        delete (this.__uids__[callback])
        callback = fn || callback
        index = arr.findIndex(f => f == callback)

        if (index > -1) {
            arr.splice(index, 1)
        }

        return this
    }

    offAll(event) {
        if (event) {
            delete (this.__callbacks__[event])
        } else {
            this.__callbacks__ = {}
        }

        return this
    }

    emit(event, ...params) {
        setTimeout(() => {
            let arr = this.__callbacks__[event] || []

            arr.forEach(callback => {
                callback.apply(null, params)
            })            
        }, 0);

        return this
    }
}
Enter fullscreen mode Exit fullscreen mode

Top comments (1)

Collapse
 
phanvantien1990 profile image
hoang duc nong

wow awesome guy !