(with GIF's!)
* actually, just a thing or two
// This is pure
function(value) {
let result = 0
// (does something with value)
return result
}
// This is not pure
let outsideVar = 12
function(value) {
// (does something with value)
let result = value + outsideVar
return result
}
Pure (all values come from inside the function) Impure(uses a value from outside the function)
let myObj = {name: 'Leo', age: '42'};
// AVOID: changing values inside an object:
myObj.age = 41;
// PREFER: recreate the object using the values:
myObj = {...myObj, age: 41}
// Or, non ES6 Style:
myObj = Object.assign({}, myObj, {age: 41})
Avoid changing the values inside your objects Prefer to recreate the object copying the values instead
From the website:
Redux is a predictable state container for JavaScript apps.
(without React)
<div class='container'>
<h1>
<span id='counter'>0</span>
</h1>
<div class='buttons'>
<button id='dec'>-</button>
<button id='inc'>+</button>
</div>
</div>
let counter = 0;
const counterDisplay = document.getElementById('counter');
const decButton = document.getElementById('dec');
const incButton = document.getElementById('inc');
decButton.addEventListener('click', () => {
counterDisplay.innerText = --counter;
})
incButton.addEventListener('click', () => {
counterDisplay.innerText = ++counter;
})
State Element Selectors Event Listeners (and state modifiers)
let defaultState = { counter: 0 }
let reducer = (state = defaultState, action) => {
switch (action.type) {
case 'INC':
return { counter: state.counter + 1 };
case 'DEC':
return { counter: state.counter - 1 };
default:
return state
}
}
Reducer: pure function that receives the current state and a action, and based on the action, returns a new state.
let store = {
state: null,
listeners: [],
getState() { return this.state },
subscribe(callback) {
this.listeners = [...this.listeners, callback]
}
dispatch(action) {
let newstate = this.reducer(this.state, action)
if (newstate !== this.state) {
this.state = newstate
this.listeners.forEach(listener => listener())
}
},
}
Creating the store and setting some default values. Just a getter method. Function that adds a callback to the list of listeners. Dispatcher function
dispatch(action) {
let newState = this.reducer(this.state, action)
if (newState !== this.state) {
this.state = newState
this.listeners.forEach(listener => listener())
}
}
Calls the reducer with the current state, and the requested action If the state has changed:
let defaultState = { counter: 0 }
let reducer = (state = defaultState, action) => {
switch (action.type) {
case 'INC':
return { counter: state.counter + 1 };
case 'DEC':
return { counter: state.counter - 1 };
default:
return state
}
}
store.reducer = reducer
let changeCounter = () => {
document.getElementById('counter').innerText = store.getState().counter
}
store.subscribe(changeCounter)
document.getElementById('dec')
.addEventListener('click', () => store.dispatch({type: 'DEC'}));
document.getElementById('inc')
.addEventListener('click', () => store.dispatch({type: 'INC'}));
Store default state Creating reducer Callback function (observer) Subscribing Event Listeners (that dispatch actions)
let store = {
state: null,
listeners: [],
getState() { return this.state },
subscribe(callback) {
this.listeners = [...this.listeners, callback]
}
dispatch(action) {
let newstate = this.reducer(this.state, action)
if (newstate !== this.state) {
this.state = newstate
this.listeners.forEach(listener => listener())
}
},
}
store.reducer = reducer
Just change this...
<script src="redux.min.js">
let store = window.Redux.createStore(reducer)
For this!
This is just a (tiny!) introduction on how to implement Redux in a small app.
In a real-world application, there are much more to do (mapDispatchToProps, createActions functions, compose reducers, middleware, memoise, ...)
yarn add redux react-redux
let defaultState = { counter: 0 }
let reducer = (state = defaultState, action) => {
switch (action.type) {
case 'ADD_TODO':
return {
...state,
todos: [...todos, action.newTodo]
}
case 'REMOVE_TODO':
return {
...state,
todos: todos.filter(todo => todo !== action.removedTodo)
};
default:
return state
}
}
import React from 'react'
import { render } from 'react-dom'
import { Provider } from 'react-redux'
import { createStore } from 'redux'
import reducer from './reducers'
import App from './components/App'
let store = createStore(reducer)
render(
<Provider store={store}>
<App />
</Provider>,
document.getElementById('root')
)
Create your store (also as seen before) Import the component Provider from react-redux Wrap your App inside the Provider
import React, {Component} from 'react'
import {connect} from 'react-redux'
(...)
class MyComponent extends Component {
(...)
handleClickIncButton = (ev) => {
this.dispatch({type: 'ADD_TODO', newTodo: this.state.newTodo})
}
(...)
render() {
const {todos} = this.props
return (...)
}
}
const mapStateToProps = (state) => ({todos: state.todos})
export default connect(MapStateToProps)(MyComponent)
First, import 'connect' from react-redux Then, create a mapping between your store and the props that will be passed Finally, connect this mapping to your component
import React, {Component} from 'react'
import {connect} from 'react-redux'
(...)
class MyComponent extends Component {
(...)
handleAddTodoButton = (ev) => {
this.dispatch({type: 'ADD_TODO', value: this.state.newTodo})
}
(...)
render() {
const {todos} = this.props
return (...)
}
}
const mapStateToProps = (state) => ({todos: state.todos})
export default connect(MapStateToProps)(MyComponent)
You can access the data from your store as a 'prop' And also can dispatch actions using 'this.props.dispatch'
Leonardo Rota-Rossi
leo@weblers.net