前言:
在项目中接触了许多状态管理相关的,redux、mobx、vuex、pinia、san-store都有使用过,但是经常都是对着别人写的代码照葫芦画瓢,懂得都懂hhh~,加一个新的store字段、action,照着更新状态就行了。现在有时间,刚好可以梳理一下,以react-redux为例,demo中的项目有两个,一个是count计数器,一个是todolist。
一、项目示例:
count计数器官网示例
todolist:官网示例
二、项目结构:
|—-entries
| |--index.tsx #项目入口
|—-components
| |--AddAsync.tsx
| |__...
|—-store
| |--actions
| | |--count.ts #计数器相关
| | |--todo.ts #todolist相关
| |--reducers
| | |--count.ts #计数器相关
| | |--todo.ts #todolist相关
| | |--index.ts #计数器相关
| |--constants.ts #定义actions类型常量
| |--index.ts #store入口
三、快速使用:
npm install redux react-redux redux-thunk -S
注:有异步操作才安装redux-thunk
3.1 store搭建:
3.1.1 actions
// store/actions/count.ts
import {
INCREMENT,
DECREMENT,
SET_PER_ADD,
INCREMENT_BY_AMOUNT,
} from '../constants';
export const addNum = num => ({
type: INCREMENT,
payload: num,
});
export const decrementNum = num => ({
type: DECREMENT,
payload: num,
});
export const addNumAsync = num => {
// 当action为函数时,Redux Thunk会调用这个函数,并传入dispatch和getState两个参数,这样我们就可以在这个函数中进行异步操作,并在需要的时候调用dispatch来触发新的action。
return dispatch => {
// return (dispatch, getState) => {
// console.log('111', getState());
// {
// "count": {
// "count": 0,
// "perAdd": 2
// }
// }
setTimeout(() => {
dispatch({
type: INCREMENT_BY_AMOUNT,
payload: num,
});
}, 2000);
};
};
export const changePerAdd = (num: number) => {
return {
type: SET_PER_ADD,
payload: +num,
};
};
- react-redux中,actions有同步和异步,同步的actions返回的是一个对象,里面有type和payload;异步的actions返回的则是一个function,当返回function时,redux thunk会向里面传入dispatch,在我们需要的时候,执行这个dispatch就可以
3.1.2 reducers
// store/reducers/count.ts
import {
INCREMENT,
DECREMENT,
SET_PER_ADD,
INCREMENT_BY_AMOUNT,
} from '../constants';
const initialState = {
count: 0,
perAdd: 2,
};
export default function countOptReducers(preState = initialState, action) {
switch (action.type) {
case INCREMENT:
return {...preState, count: preState.count + 1};
case DECREMENT:
return {...preState, count: preState.count - 1};
case INCREMENT_BY_AMOUNT:
return {...preState, count: preState.count + preState.perAdd};
case SET_PER_ADD:
return {...preState, perAdd: action.payload};
default:
return preState;
}
}
3.1.3 constants.ts
// store/constants.ts
// 计数器相关
export const INCREMENT = 'increment';
export const INCREMENT_BY_AMOUNT = 'increment_by_amount';
export const DECREMENT = 'decrement';
export const SET_PER_ADD = 'change_per_add';
// TODO列表相关
export const ADD_TODO = 'add_todo';
export const CHANGE_STATUS = 'change_status';
3.1.4 reducers合并
// store/reducers/index.ts
import {combineReducers} from 'redux';
import count from './count';
import todo from './todo';
export default combineReducers({
count,
todo,
});
3.2 store入口
store/index.ts
import {legacy_createStore as createStore, applyMiddleware} from 'redux';
// 支持异步action
import {thunk} from 'redux-thunk';
import rootReducer from './reducers';
export default createStore(rootReducer, applyMiddleware(thunk));
- 需要用thunk中间件,才能支持异步action
store使用:
前面已经把store搭建好了,下面讲述如何使用store
3.2.1 根组件使用provide包裹
使用provider,将store提供跟根组件,原理是使用react context
import {StrictMode} from 'react';
import {render} from 'react-dom';
import {BrowserRouter} from 'react-router-dom';
import {Provider} from 'react-redux';
import store from '@/store'; //store/index.ts
import App from '@/modules/App';
import '@/styles';
const main = () => render(
<Provider store={store}>
<StrictMode>
<BrowserRouter>
<App />
</BrowserRouter>
</StrictMode>
</Provider>,
document.body.appendChild(document.createElement('div'))
);
main();
3.2.2 子组件使用
由于我们使用的是react hook,有两种方式,子组件可以获取修改store,一种是使用connect,一种是使用hooks
方法一:使用connect
/**
* @file 每次增加的个数
*/
import {useState} from 'react';
import {connect} from 'react-redux';
import {changePerAdd} from '@/store/actions/count';
import styles from './index.module.less';
function AddNum({value, changePerAdd}) {
const {perAdd} = value.count;
const [addNum, setAddNum] = useState(perAdd);
const handleChange = (e: any) => {
setAddNum(e.target.value);
changePerAdd(e.target.value);
};
return (
<div className={styles.addNum}>
<input value={addNum} onChange={handleChange} />
</div>
);
}
export default connect(
state => ({value: state}),
{changePerAdd}
)(AddNum);
- provide只是确保了子组件可以获取store实例,但是子组件需要通过connect才能获取到store
方法二:使用hooks
/**
* @file 每次增加的个数
*/
import {useState} from 'react';
import {useSelector, useDispatch} from 'react-redux';
import {changePerAdd} from '@/store/actions/count';
import styles from './index.module.less';
export default function AddNum() {
// 通过hook获取store中的数据
const storeCount = useSelector(state => state.count.perAdd);
// 通过hook获取store中的dispatch方法
const dispatch = useDispatch();
const [addNum, setAddNum] = useState(storeCount);
const handleChange = (e: any) => {
setAddNum(e.target.value);
dispatch(changePerAdd(e.target.value));
};
return (
<div className={styles.addNum}>
<input value={addNum} onChange={handleChange} />
</div>
);
}