This is a code example of Redux with only HTML & pure JavaScript. Code sandbox
<!DOCTYPE html>
<html>
<head>
<title>Redux basic example</title>
<script src="https://unpkg.com/redux@latest/dist/redux.min.js"></script>
</head>
<body>
<div>
<p>
Clicked: <span id="value">0</span> times
<button id="increment">+</button>
<button id="decrement">-</button>
<button id="incrementIfOdd">Increment if odd</button>
<button id="incrementAsync">Increment async</button>
</p>
</div>
<script>
function counter(state, action) {
if (typeof state === 'undefined') {
return 0
}
switch (action.type) {
case 'INCREMENT':
return state + 1
case 'DECREMENT':
return state - 1
default:
return state
}
}
var store = Redux.createStore(counter)
var valueEl = document.getElementById('value')
function render() {
valueEl.innerHTML = store.getState().toString()
}
render()
store.subscribe(render)
document.getElementById('increment')
.addEventListener('click', function () {
store.dispatch({ type: 'INCREMENT' })
})
document.getElementById('decrement')
.addEventListener('click', function () {
store.dispatch({ type: 'DECREMENT' })
})
document.getElementById('incrementIfOdd')
.addEventListener('click', function () {
if (store.getState() % 2 !== 0) {
store.dispatch({ type: 'INCREMENT' })
}
})
document.getElementById('incrementAsync')
.addEventListener('click', function () {
setTimeout(function () {
store.dispatch({ type: 'INCREMENT' })
}, 1000)
})
</script>
</body>
</html>
-
createStore
&counterReducer
// Counter reducer
function counterReducer(state, action) {
if (typeof state === 'undefined') {
return 0;
}
switch (action.type) {
case 'INCREMENT':
return state + 1;
case 'DECREMENT':
return state - 1;
default:
return state;
}
}
// Create store
var store = Redux.createStore(counterReducer);
-
createStore
receives acounterReducer
function as a param and return an object called store. - This is the diagram of createStore function with mental model as a class.
Here is simplified version of createStore
in redux source code:
function createStore(reducer, initialState) {
var currentReducer = reducer;
var currentState = initialState;
var listeners = [];
var isDispatching = false;
function getState() {
return currentState;
}
function subscribe(listener) {
listeners.push(listener);
return function unsubscribe() {
var index = listeners.indexOf(listener);
listeners.splice(index, 1);
};
}
function dispatch(action) {
if (isDispatching) {
throw new Error('Reducers may not dispatch actions.');
}
try {
isDispatching = true;
currentState = currentReducer(currentState, action);
} finally {
isDispatching = false;
}
listeners.slice().forEach(listener => listener());
return action;
}
function replaceReducer(nextReducer) {
currentReducer = nextReducer;
dispatch({ type: '@@redux/INIT' });
}
dispatch({ type: '@@redux/INIT' });
return { dispatch, subscribe, getState, replaceReducer };
}
-
currentReducer
=counterReducer
-
currentState
=preloadedSate
- When store is created, it initially
dispatch
with action type is'@@redux/INIT'
so that every reducer returns their initial state. In casecounterReducer
, it returns0
What happens inside dispatch
function ?
// Dispatch function inside Redux store
function dispatch(action: A) {
currentState = currentReducer(currentState, action)
const listeners = (currentListeners = nextListeners)
for (let i = 0; i < listeners.length; i++) {
const listener = listeners[i]
listener()
}
return action
}
- The function
currentReducer
is called which iscounterReducer
- Because action type is
@@redux/INIT
andcurrentState
isundefined
, socounterReducer
returns0
as default value which is the initial state of the store. - Now,
currentState
is0
- After updating the state with initial value, it calls all listeners that is subscribing the store to notify.
var valueEl = document.getElementById('value')
function render() {
valueEl.innerHTML = store.getState().toString()
}
render()
store.subscribe(render)
- In this case, we have
render()
function, it is called back and update the DOM element with the initial value. - Now in the browser, we will se the number
0
shown.
Updating state when action is sent
document.getElementById('increment')
.addEventListener('click', function () {
store.dispatch({ type: 'INCREMENT' })
})
- When users click on the button "+", store dispatches the action with type
'INCREMENT'
to the reducer of the store and the flow is the same as explanation above. - Function
currentReducer
is called with state is0
and action's type is'INCREMENT'
. - Because
'INCREMENT'
is a case insidecounterReducer
function, so the new state now is equal to0 + 1
and returned to the state of the store. - Next, again it notifies listeners to let them know state is updated successfully.
- Now, in the screen we will see Clicked: 1 times
- The flow is similar to other action types
So this is basically how Redux works under the hood. In real life project, Redux store may have multiple reducers
and midleware
, and 3rd-party libraries enhance Redux workflow. But at very its core that's how it works basically !
Top comments (3)
I use index.js file that contains in script tags codes. However, getting error Redux.create is not a function.
Try this link codesandbox.io/s/github/reduxjs/re...
I want to create a another js file for script tag but getting error. I don't want to any script code in index.html but unpkg.com/redux@latest/dist/redux.... isn't reached other js file