Собственный redux, проблема с отслеживанием изменений
Всем привет. Столкнулся с интересной задачей. Нужно написать собственную реализацию редакса и хуки useDispatch и useSelector. Столкнулся с проблемой отслеживание изменений стора. Ссылка на песочницу Может кто-то направить на путь истинный?
const createStore = (reducer, initialState) => {
let currentState = initialState
let currentReducer = reducer
var listeners = []
const getState = () => currentState
const dispatch = action => {
currentState = reducer(currentState, action)
listeners.forEach(listener => listener())
return action
}
const subscribe = listener => listeners.push(listener)
return { getState, dispatch, subscribe }
}
const Context = React.createContext(null);
const useSelector = selector => {
const ctx = React.useContext(Context)
if (!ctx) {
return 0
}
return selector(ctx.store.getState())
}
const useDispatch = (action) => {
const ctx = React.useContext(Context)
if (!ctx) {
return () => {}
}
return ctx.store.dispatch
}
const Provider = ({ store, children }) => {
return <Context.Provider value={{ store }}>{children}</Context.Provider>
}
// APP
// actions
const UPDATE_COUNTER = 'UPDATE_COUNTER'
const CHANGE_STEP_SIZE = 'CHANGE_STEP_SIZE'
// action creators
const updateCounter = value => ({
type: UPDATE_COUNTER,
payload: value,
})
const changeStepSize = value => ({
type: CHANGE_STEP_SIZE,
payload: value,
})
// reducers
const defaultState = {
counter: 1,
stepSize: 1,
}
const reducer = (state = defaultState, action) => {
switch(action.type) {
case UPDATE_COUNTER:
return { ...state, counter: state.counter += action.payload }
case CHANGE_STEP_SIZE:
return { ...state, stepSize: action.payload }
default:
return state
}
}
const Counter = () => {
const counter = useSelector(state => state.counter)
const dispatch = useDispatch()
return (
<div>
<button onClick={() => dispatch(updateCounter(-1))}>-</button>
<span> {counter} </span>
<button onClick={() => dispatch(updateCounter(1))}>+</button>
</div>
)
}
const Step = () => {
const stepSize = useSelector(state => state.stepSize, (current, prev) => current === prev)
const dispatch = useDispatch()
return (
<div>
<div>Значение счётчика должно увеличиваться или уменьшаться на заданную величину шага</div>
<div>Текущая величина шага: {stepSize}</div>
<input
type="range"
min="1"
max="5"
value={stepSize}
onChange={({ target }) => dispatch(changeStepSize(target.value))}
/>
</div>
)
}
const store = createStore(reducer, defaultState);
ReactDOM.render(
<Provider store={store}>
<Step />
<Counter />
</Provider>,
document.getElementById('app')
)