Redux
When we want to use some data in the whole app, we can pass it by query parmeters
or props
. But it's not convenient.
Redux is what you need.
install
npm install redux react-redux
Basic
Redux is a state management tool. It's independant to React. It has some concepts.
Store
Store saves all the states in App. Only Reducers can update it.
const store = redux.createStore(rootReducer);
Reducers
It updates the store according to the actions.
Attention: current state is based on the copy of previous state
// store/reducer.js
const rootReducer = (state = initialState, action) => {
if (action.type === 'ADD_COUNTER') {
return {
...state,
counter: state.counter + action.value
};
}
return state;
};
Actions
Actions tell the Reducers how to update the Store. It can be dispatched.
const action = {type: 'ADD_COUNTER', value: 10};
store.dispatch({type: 'ADD_COUNTER', value: 10});
subscribe
Once Store is updated, it will tell all the components which subscribe it.
store.subscribe(() => {
console.log('[Subscription]', store.getState());
});
react-redux
-
<Provider>
: inject Redux store into React// index.js const store = createStore(reducer); ReactDOM.render(<Provider store={store}><App /></Provider>);
-
connect: a HOC function
-
state
// Counter.js // pass state in store to component as props const mapStateToProps = state => { return { ctr: state.counter } } export default connect(mapStateToProps)(Counter);
// Counter.js // in the Counter class <div>{this.props.ctr}</div>
-
dispatch
// Counter.js // pass dispatch in store to component as props const mapDispatchToProps = dispatch => { return { onMyClick: () => dispatch({type: 'CLICK'}); } } export default connect(mapStateToProps, mapDispatchToProps)(Counter);
// Counter.js // in the Counter class <button onClick={this.props.onMyClick}></button>
-
Multi Reducers
use combineReducers
to combine multi reducers into one
const rootReducer = combineReducers({
ctr: counterReducer,
res: resultReducer,
});
const store = createStore(rootReducer);
To access counterReducer
, we use state.ctr
// Counter.js
const mapStateToProps = state => {
return {
ctr: state.ctr.counter
}
}
However, in counterReducer.js
, we can't access the state
.
update state immutably
In Redux, always create a new state based on the old one.
There are two ways:
-
Object.assign
const newState = Object.assign({}, state); newState.counter = state.counter + 1; return newState
-
spread operator
return { ...state, counter: state.counter + 1 };
manipulate the array
function insertItem(array, action) {
let newArray = array.slice()
newArray.splice(action.index, 0, action.item)
return newArray
}
function removeItem(array, action) {
let newArray = array.slice()
newArray.splice(action.index, 1)
return newArray
// or
return array.filter((item, index) => index !== action.index)
}
function updateObjectInArray(array, action) {
return array.map((item, index) => {
if (index !== action.index) {
// This isn't the item we care about - keep it as-is
return item
}
// Otherwise, this is the one we want - return an updated value
return {
...item,
...action.item
}
})
}