advanced
by Elizaveta Anatskaya
Think of your state as a tree. It has branches which have their own branches and leaves, and so on. A subtree is one branch of that tree.
state = {
profile: {
name: 'John Jones',
private: true
},
photos: [
]
}
//state.profile is a subtree of the state tree.
combineReducers() lets you break apart your reducer. Your reducers will only operate in their own subtree, like state.profile above. This reducer can't see state.photos!
This is usually a good thing. As your store reducers get bigger, you're assured that they only play in one part of your state tree.
function profile (state, action) {
if (action.type === 'PUBLISH') {
//state is limited to state.profile here.
return { ...state, private: false }
}
}
let reducer = combineReducers({ profile, })
Let's say your app has articles and users. If we put everything into one reducer, we'll be writing a very long function! There's a better way. Imagine your store state looks like this:
{
articles: {},
users: {}
}
You can use combineReducers to make two reducers, each working on one part. This is only useful if each reducer works on a single part of your state.
import { combineReducers, createStore } from 'redux'
function articles (state, action) {
...state is state.articles here.
}
function users (state, action) {
...state is state.users here.
}
let reducer = combineReducers({ articles, users })
let store = createStore(reducer)
The combineReducers helper function turns an object whose values are different reducing functions into a single reducing function you can pass to createStore. The resulting reducer calls every child reducer, and gathers their results into a single state object.
#Arguments
reducers (Object): An object whose values correspond to different reducing functions that need to be combined into one.
#Returns
(Function): A reducer that invokes every reducer inside the reducers object, and constructs a state object with the same shape.
react-redux is the official package used to make Redux and React work together. To use it, wrap your main app (your top-most React component) inside a <Provider>. This lets your components see the store.
However, in our Redux App we will still need <Provider />. <Provider /> is the higher-order component provided by React Redux that lets you bind Redux to React
import React from 'react'
import { render } from 'react-dom'
import { createStore } from 'redux'
import { Provider } from 'react-redux'
let store = createStore(todoApp)
render(
<Provider store={store}>
<App />
</Provider>,
document.getElementById('root'))
connect() – is a HOC
Pass 2 things:
1. What parts of global the state does the component needs access to?
2. What actions do you want to be able to dispatch?
It then returns a function to which you pass the component you want to connect. When called, this function creates a new component wrapping yours which passes the global state and “dispatchable” actions to your component via props.
"connect(“What parts of state do you want?”, “What actions do you want to dispatch?”)(Component)
react-redux provides connect() to let components see the store's state.
connect() is a function that connects a React component to a Redux store.
function connect(mapStateToProps?, mapDispatchToProps?, mergeProps?, options?)
It provides its connected component with the pieces of the data it needs from the store, and the functions it can use to dispatch actions to the store
import { connect } from 'redux'
PhotosList = React.createClass({ })
PhotosList = connect(mapState)(PhotosList)
What's mapState? It takes the state and returns props to be used by the component. Write this function and pass it to connect().
function mapState (state) {
return { photos: state.photos }
}
You can use the props you made in mapState() inside your component as this.props.
var PhotosList = React.createClass({
render () {
let photos = this.props.photos
return <div>{photos.map()}</div>
}
})
function mapStateToProps(globalState, ownProps?) {
// return an object where the keys are the name of the prop your component wants,
// values are the actual parts of the global state your component wants
}
is used for selecting the part of the data from the store that the connected component needs.
(state) => stateProps | (state, ownProps) => stateProps | |
---|---|---|
mapStateToProps runs when: | store state changes | store state changes or any field of ownProps is different |
component re-renders when: | any field of stateProps is different | any field of stateProps is different or any field of ownProps is differen |
You may define the function with a second argument, ownProps, if your component needs the data from its own props to retrieve data from the store. This argument will contain all of the props given to the wrapper component that was generated by connect.
function mapStateToProps(state, ownProps) {
const { visibilityFilter } = state
const { id } = ownProps
const todo = getTodoById(state, id)
// component receives additionally:
return { todo, visibilityFilter }
}
// Later, in your application, a parent component renders:
<ConnectedTodo id={123} />
// and your component receives props.id, props.todo, and props.visibilityFilter
You do not need to include values from ownProps in the object returned from mapStateToProps. connect will automatically merge those different prop sources into a final set of props.
connect() also lets you send props to your Components that map to dispatch calls. The 2nd argument of connect() lets you do this.
var PhotosList = connect(mapState, mapDispatch)(PhotosList)
Provide it a function that returns properties to be added to your component. These properties are functions that you can call later on.
function mapDispatch (dispatch) {
return {
onPublishClick: function () {
dispatch({ type: 'PUBLISH' })
}
}
}
These properties will be available in props in your component. You can then call them on certain events like onClick.
<button onClick={() => this.props.onPublishClick()}>
– is a function of the Redux store. You call store.dispatch to dispatch an action. This is the only way to trigger a state change.
With React Redux, your components never access the store directly - connect does it for you. React Redux gives you two ways to let components dispatch actions:
const increment = () => ({ type: 'INCREMENT' })
const decrement = () => ({ type: 'DECREMENT' })
const reset = () => ({ type: 'RESET' })
const mapDispatchToProps = dispatch => {
return {
// dispatching actions returned by action creators
increment: () => dispatch(increment()),
decrement: () => dispatch(decrement()),
reset: () => dispatch(reset())
}
}
render() {
return <button onClick={() => this.props.toggleTodo()} />
}
const mapDispatchToProps = (dispatch, ownProps) => {
return {
toggleTodo: () => dispatch(toggleTodo(ownProps.todoId))
}
}
const store = createStore(rootReducer)
ReactDOM.render(
<Provider store={store}>
<App />
</Provider>,
document.getElementById('root')
)
import React from 'react'
import { useSelector } from 'react-redux'
export const CounterComponent = () => {
const counter = useSelector(state => state.counter)
return <div>{counter}</div>
}
gets the data from the Redux store in a React component.
It takes 2 arguments. The first argument is a function that returns the state, and the second argument is a function that checks if the previous and current state are equal to determine when to update.
import React from 'react'
import { useDispatch } from 'react-redux'
export const CounterComponent = ({ value }) => {
const dispatch = useDispatch()
return (
<div>
<span>{value}</span>
<button onClick={() => dispatch({ type: 'increment-counter' })}>
Increment counter
</button>
</div>
)
}
returns a reference to the dispatch function from the Redux store. You may use it to dispatch actions as needed.
The equivalent of map dispatch to props is useDispatch. We will invoke useDispatch and store it to a variable, dispatch. Dispatch will work with the allActions imported from the actions folder.
👾👾👾