react-redux项目快速使用

1,778 阅读3分钟

前言:

在项目中接触了许多状态管理相关的,redux、mobx、vuex、pinia、san-store都有使用过,但是经常都是对着别人写的代码照葫芦画瓢,懂得都懂hhh~,加一个新的store字段、action,照着更新状态就行了。现在有时间,刚好可以梳理一下,以react-redux为例,demo中的项目有两个,一个是count计数器,一个是todolist。

一、项目示例:

count计数器官网示例

image.png todolist:官网示例

image.png

二、项目结构:

|—-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>
    );
}