Problems when manage global state with hooks and context api:
Preventing rerenders with React.memo and useContext hook.
State Management with React Hooks — No Redux or Context API
useGlobalState.ts
import { useState, useEffect, Dispatch, SetStateAction } from 'react'
type id<T> = (a: T) => T
export default function useGlobalState<T>(initialState: T) {
let state = initialState
let listeners: Array<Dispatch<SetStateAction<T>>> = []
const setState = (newState: T | id<T>) => {
if (typeof newState === 'function') {
state = (newState as id<T>)(state)
} else {
state = newState as T
}
listeners.forEach(listener => {
listener(state)
})
}
const useCustom: () => [T, (state: T | id<T>) => void] = () => {
const newListener = useState(initialState)[1]
useEffect(() => {
listeners.push(newListener)
return () => {
listeners = listeners.filter(listener => listener !== newListener)
}
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [])
return [state, setState]
}
return useCustom
}
store.ts
import useGlobalState from './useGlobalState'
export const useGlobalCount = useGlobalState(0)
A.tsx
import React from 'react'
import { useGlobalCount } from '../store'
const A: React.FC = () => {
const [count, setCount] = useGlobalCount()
return (
<div>
<span>A {count}</span>
<button onClick={() => setCount(count + 1)}> + </button>
</div>
)
}
export default A
B.tsx
import React from 'react'
import { useGlobalCount } from '../store'
const B: React.FC = () => {
const [count, setCount] = useGlobalCount()
return (
<div>
<span>B {count}</span>
<button onClick={() => setCount(count - 1)}> - </button>
</div>
)
}
export default B
And you can also pass a object to useGlobalState
and wrap it with mutations and actions in store.ts
.