Application state management
Flux is a pattern that Facebook uses for
building front-end web applications.
It complements React's components with
a unidirectional data flow.
Flux
Flux
Motivation...
Flux
Flux
Flux
Flux
Flux is not a library
It is just a pattern
a guideline...
Alt
DeLorean.js
Flummox
McFly
marty.js
RefluxJS
Fluxible
material-flux
🎉
🥇
🥇
Single source of truth
all application state is stored as
a single JS object
store
1st Principle
You cannot write to or modify the app state directly
store
2nd Principle
Actions are payloads of information
They are the only source of data of the store.
const ADD_TODO = 'ADD_TODO'
{
type: ADD_TODO,
text: 'Build my first Redux app'
}
Action Creators are functions
that create and return actions
const ADD_TODO = 'ADD_TODO'
function addTodo(text) { return { type: ADD_TODO, text: text } }
3rd Principle Changes are made with pure functions
A reducer is a pure function that takes the previous state and an action, and returns the next state.
Reducers Handle Actions
Handling Actions in Reducers
let initialState = { todos: [], visibilityFilter: 'ALL' }
function todosReducer(reducerState = initialState, action){ switch (action.type) { case SET_VISIBILITY_FILTER: return { ...reducerState, visibilityFilter: action.filter } case ADD_TODO: return { ...reducerState, todos: [...reducerState.todos,{ text: action.text, completed: false }] } default: return reducerState
}
}
Combine Reducers
reducers/index.js import { combineReducers } from 'redux' import todosReducer from './todos.reducer.js' import countReducer from './count.reducer.js'
const rootReducer = combineReducers({
todos : todosReducer,
count : countReducer
})
export default rootReducer
To create the store instance
use the redux method createStore()
createStore() requires a root reducer as 1st param
and optionally - an initialState and an enhancer
import { createStore, applyMiddleware } from 'redux'
import rootReducer from './reducers'
let store = createStore(rootReducer ,
{count:8} ,
applyMiddleware(...))
Middleware are functions used to extend Redux
They are composable by design
Each function requires no knowledge of what comes before or after it in the chain.
Middleware functions have the following signature:
import { createStore, applyMiddleware } from 'redux'
import rootReducer from './reducers' const logger = store => next => action => { console.log('dispatching', action) next(action) console.log('next state', store.getState() }
let store = createStore(rootReducer , {count:8} , applyMiddleware(logger) )
const logger = store => next => action => {
console.log('dispatching', action)
next(action)
console.log('next state', store.getState())
}
function logger(store) {
return function(next) {
return function(action) {
console.log('dispatching', action)
next(action)
console.log('next state', store.getState())
}
}
}
Same as writing:
Same function signature as home-made middleware
serve as Redux 'plugins' and available as npm packages
import { createStore, applyMiddleware } from 'redux'
import thunk from 'redux-thunk'
import rootReducer from './reducers'
import logger from './my-middleware/logger'
let store = createStore(rootReducer , {count:8} , applyMiddleware(thunk,logger) )
regular Action Creator functions
create and return action objects
const ADD_TODO = 'ADD_TODO'
function addTodo(text) { return { type: ADD_TODO, payload: text } }
Async action creators return a callback with a promise
The callback will receive the store dispatch method as a parameter
* The following code will work only if redux-thunk middleware was used at the store creation
function fetchPosts(topic) {
return async (dispatch)=> {
try{
dispatch(fetch_posts_start(topic)) // START
const url = `https://myblog.com/api/${topic}`
const response = await fetch(url) const data = await response.json() dispatch(fetch_posts_success(topic, data)) // SUCCESS
}catch(error){
dispatch(fetch_posts_failed(error)) // FAILED }
}
}
Middleware are functions used to extend Redux
They are composable by design
Each function requires no knowledge of what comes before or after it in the chain.
You can write Redux apps with React, Angular, Ember, jQuery, or vanilla JavaScript.
<Provider store>
Injects the store to your app
hooks API
The hooks API is an elegant way to interact with the store from react
Recommended!
Connect(...)
HOC connect to store from within react component outdated,
not recommended...
Wraps the root component,
Providing access to the Redux store
ReactDOM.render(
<Provider store={store}>
<MyRootComponent />
</Provider>,
document.getElementById('root')
)
Rerender on store state changes
Affect the store state
import React from 'react'
import { useSelector, useDispatch } from 'react-redux'
export const CounterComponent = () => {
const counter = useSelector(state => state.counter)
const dispatch = useDispatch()
return (
<div>
<span>{counter}</span>
<button onClick={() => dispatch({ type: 'increment' })}>
Increment counter
</button>
</div>
)
}
import { connect } from 'react-redux'
let ProfilePage = () => {
return (
<div>
...
</div>
)
}
export default connect(...)(ProfilePage)
To use connect(), define a mapStateToProps function
It maps the parts of the store state your container cares about
into props it will pass down to its child components
import { connect } from 'react-redux'
let ListView = ({ active }) => {
return <div>...</div>
}
const mapStateToProps = (state, ownProps) => {
return {
active: ownProps.filter === state.visibilityFilter
}
}
export default connect(mapStateToProps)(ListView)
import { someActionCreator,increment,decrement,toggleTodo} from './actions'
let ListView = ({ doSomething, doIncrement, doDecrement, onTodoClick}) => {
return <div>...</div>
}
const mapDispatchToProps = (dispatch, ownProps) => {
return {
doSomething: ()=> dispatch( someActionCreator() )
doIncrement: ()=> dispatch( increment() ), doDecrement: ()=> dispatch( decrement() ),
onTodoClick: (id)=> dispatch( toggleTodo(id) )
}
}
export default connect(mapStateToProps, mapDispatchToProps)(ListView)
Optional second parameter of connect(),
It recieves dispatch() as a param and returns callback props
your container will typically inject down to its child components
* if not implemented, containers will have dispatch() available via props