最新公告
  • 欢迎您光临起源地模板网,本站秉承服务宗旨 履行“站长”责任,销售只是起点 服务永无止境!立即加入钻石VIP
  • redux 学习总结

    正文概述 掘金(好_快)   2021-08-19   563
    • 准备 React 开发环境
    • react 实现功能,发现 State 数据分散在各个组件。在某些场景下不易维护,
    • 使用 redux 重构,实现集中化管理 State。发现组件和 store 通信麻烦,存在重复样板代码
    • 使用 react-redux 重构,优化代码
    • 使用 hooks 重构,进一步优化代码,异步处理还放在组件中
    • 使用 redux-thunk 重构,支持异步任务处理,如网络请求等。出现配置麻烦、处理可变数据、代码逻辑分散等问题
    • 使用 @reduxjs/toolkit 重构

    一、准备环境

    1、使用脚手架创建工程

    npx create-react-app react_typescript --template typescript
    

    2、安装 react-router-dom

    yarn add react-router-dom
    

    3、 安装 antd

    yarn add antd
    
    

    修改 src/index.css,在文件顶部引入 antd 的样式。

    @import '~antd/dist/antd.css';
    

    4、安装 prettier

    yarn add prettier -D
    
    <!--.prettierrc.js-->
    module.exports = {
        trailingComma: "es5", //尾随逗号
        tabWidth: 4,
        semi: true, //句尾分号
        singleQuote: false, //不允许单引号
    };
    

    5、安装 axios

    yarn add axios
    

    二、react 实现功能

    1、帖子相关

    • 帖子列表:id、title、content、user、date
    • 帖子操作:反应表情
    • 新增帖子
    • 编辑帖子
    • 帖子详情

    2、用户相关

    • 用户列表:id、name

    3、示例代码

    以帖子列表页面为例,流程

    1. 获取数据
    2. 更新state
    3. 触发重新渲染
    4. 页面更新
    /**
     * 帖子列表
     * */
    const PostListPage = ({ history }: Props) => {
        const [data, setData] = useState<Post_dao[]>([]);
        useEffect(() => {
            ajax.get("/data/postList.json")
                .then((res: AxiosResponse<Post_dao[]>) => {
                    console.log(res);
                    const { data } = res;
                    if (data) {
                        setData(data);
                    }
                })
                .catch((error) => {
                    message.error(error.message);
                });
        }, []);
    
        return (
            <div className={styles.main}>
                <Typography.Title level={3}>帖子列表</Typography.Title>
                <List
                    dataSource={data}
                    renderItem={(item) => (
                        <List.Item>
                            <Post
                                key={item.id}
                                data={item}
                            />
                        </List.Item>
                    )}
                    grid={{
                        gutter: 16,
                        column: 1,
                    }}
                />
            </div>
        );
    };
    export default PostListPage;
    

    三、 redux 重构

    redux 引入3个概念:store、reducer、action。流程

    1. 组件订阅 store
    2. 组件--> store--> dispatch(action)--> reducer处理--> store变更 --> store 通知组件变更--> 组件更新 State
    3. 触发组件重新渲染
    4. 页面更新
    <!--action-->
    export const PostListType = "postList";
    export const postList_action = (data: Post_dao[]) => {
        return {
            type: PostListType,
            data,
        };
    };
    <!--reducer-->
    const posts = (state = [], action: AnyAction) => {
        switch (action.type) {
            case PostListType: {
                return [...action.data];
            }
            default: {
                return state;
            }
        }
    };
    
    export default posts;
    
    <!--组件-->
    const PostListPage = ({ history, store }: Props) => {
        const [data, setData] = useState<Post_dao[]>([]);
    
        useEffect(() => {
            const updateData = () => {
                //数据绑定
                const allState = store.getState();
                const { posts } = allState;
                if (posts) {
                    if (posts.length <= 0) {
                        ajax.get("/data/postList.json")
                            .then((res: AxiosResponse<Post_dao[]>) => {
                                const { data } = res;
                                if (data) {
                                    store.dispatch(postList_action(data));
                                }
                            })
                            .catch((error) => {
                                message.error(error.message);
                            });
                    } else {
                        setData(posts);
                    }
                }
            };
            const unsubscribe = store.subscribe(updateData);
            updateData();
            return () => {
                unsubscribe();
            };
        }, [store]);
    
        const onSave = (post: any) => {
            store.dispatch(postNew_action(post));
        };
    
    };
    export default PostListPage;
    

    每个用到 store 的组件都要处理store数据映射问题:

    1. 订阅和取消订阅
    2. 从 allState 中读取到 需要的数据

    四、react-redux 重构

    react-redux 优化了 store 和 组件绑定的体验。

    • 通过 connect() 在 store 和 组件之间添加一个处理层函数mapStateToProps,把数据通过props传递给组件,优化订阅问题
    • 把从 allstate 读取数据逻辑,转移到mapStateToProps,使得组件更纯粹

    4.1 实现思路

    然后流程

    1. store 通过 connect()绑定组件
    2. 组件--> store--> dispatch(action)--> reducer处理--> store变更 --> mapStateToProps-->更新props--> 组件更新 State
    3. 触发组件重新渲染
    4. 页面更新
    const PostListPage = (props: Props) => {
        const { history, posts, dispatch } = props;
        useEffect(() => {
            const updateData = () => {
                if (posts) {
                    if (posts.length <= 0) {
                        ajax.get("/data/postList.json")
                            .then((res: AxiosResponse<Post_dao[]>) => {
                                const { data } = res;
                                if (data) {
                                    dispatch(postList_action(data));
                                }
                            })
                            .catch((error) => {
                                message.error(error.message);
                            });
                    }
                }
            };
    
            updateData();
        }, [posts, dispatch]);
    
    };
    
    //数据绑定
    const mapStateToProps = (state: any) => {
        return { posts: state.posts };
    };
    
    export default connect(mapStateToProps)(PostListPage);
    

    4.2 hooks 重构

    使用 react-redux 提供的 hooks 函数,可以省去 connect(),进一步优化代码

    然后流程

    1. 组件--> store--> dispatch(action)--> reducer处理--> store变更 --> useSelector
    2. 组件重新渲染
    3. 页面更新
    const PostListPage = (props: Props) => {
        const { history } = props;
        const dispatch = useDispatch();
        //数据绑定
        const posts: Post_dao[] = useSelector((state: any) => {
            return state.posts;
        });
    
        useEffect(() => {
            const updateData = () => {
                if (posts) {
                    if (posts.length <= 0) {
                        ajax.get("/data/postList.json")
                            .then((res: AxiosResponse<Post_dao[]>) => {
                                const { data } = res;
                                if (data) {
                                    dispatch(postList_action(data));
                                }
                            })
                            .catch((error) => {
                                message.error(error.message);
                            });
                    }
                }
            };
    
            updateData();
        }, [posts, dispatch]);
    
    };
    
    export default PostListPage;
    

    虽然已经简化了代码,但是目前还没有支持异步任务处理。关于异常任务的中间件选择 thunks-sagas-observables,官方建议先 thunk 然后再 saga。

    五、redux-thunk 重构

    然后流程

    1. 组件--> store--> dispatch(异步action)--> 执行异步任务 --> dispatch(同步action)
    2. reducer处理--> store变更 --> useSelector
    3. 组件重新渲染
    4. 页面更新
    <!--1.导入中间件-->
    <!--milddleware.ts-->
    const middlewareEnhancer = applyMiddleware(reduxThunk);
    export default middlewareEnhancer;
    <!--2.使用中间件-->
    <!--store.ts -->
    import { createStore } from "redux";
    import reducer from "./reducer";
    import { composeWithDevTools } from "redux-devtools-extension";
    import middlewareEnhancer from "./milddleware";
    export default createStore(reducer, composeWithDevTools(middlewareEnhancer));
    <!--3.添加异步action-->
    export const postList_action_a = () => {
        return (dispatch: Dispatch, getState: any) => {
            const state = getState();
    
            if (state.posts.list && state.posts.list.length <= 0) {
                dispatch(postStatus_action(PostStatus.loading, null));
    
                return ajax
                    .get("/data/postList.json")
                    .then((res: AxiosResponse<Post_dao[]>) => {
                        setTimeout(() => {
                            dispatch(postStatus_action(PostStatus.success, null));
                            const { data } = res;
                            if (data) {
                                dispatch(postList_action(data));
                            } else {
                                dispatch(postList_action([]));
                            }
                        }, 2000);
                    })
                    .catch((error) => {
                        dispatch(
                            postStatus_action(PostStatus.failed, error.message)
                        );
                    });
            }
        };
    };
    
    export const PostListType = "postList";
    export const postList_action = (data: Post_dao[]) => {
        return {
            type: PostListType,
            data,
        };
    };
    <!--4.重构组件-->
    const PostListPage = (props: Props) => {
        const { history } = props;
        const dispatch = useDispatch();
        const posts: Post_dao[] = useSelector((state: any) => {
            return state.posts.list;
        });
        const status: PostStatus = useSelector((state: any) => {
            return state.posts.status;
        });
    
        useEffect(() => {
            const updateData = () => {
                dispatch(postList_action_a());
            };
    
            updateData();
        }, [posts, dispatch]);
    
    
    };
    export default PostListPage;
    

    六、@reduxjs/toolkit 重构

    流程 同 redux-thunk 没有变化,主要优化了redux本身使用代码。

    • 简化了 store 配置
    • 合并 action 和 reducer 到一个 slice 文件中
    • 简化不可变数据处理
    • 自动生成大部分 action
    • reducer 自动匹配,避免 switch 语句
    <!--reducer.ts-->
    import posts from "../page/post/slice/postSlice";
    import users from "../page/post/slice/userSlice";
    export default combineReducers({ posts, users });
    <!--简化了 store 配置-->
    <!--store.ts-->
    import reducer from "./reducer";
    import { configureStore } from "@reduxjs/toolkit";
    export default configureStore({ reducer });
    <!--合并 action 和 reducer-->
    <!--postSlice.ts-->
    export const postSlice = createSlice({
        name: "posts",
        initialState,
        reducers: {
            postStatus: (
                state: any,
                action: PayloadAction<{
                    status: PostStatus;
                    message: string | null;
                }>
            ) => {
                state.status = action.payload.status;
                state.message = action.payload.message;
            },
            postList: (state: any, action: PayloadAction<Post_dao[]>) => {
                <!--简化不可变数据处理-->
                state.list = action.payload;
            },
    
        },
    });
    export const postList_a = () => {
        return (dispatch: Dispatch, getState: any) => {
            const state = getState();
    
            if (state.posts.list && state.posts.list.length <= 0) {
                dispatch(postStatus({ status: PostStatus.loading, message: null }));
    
                return ajax
                    .get("/data/postList.json")
                    .then((res: AxiosResponse<Post_dao[]>) => {
                        setTimeout(() => {
                            dispatch(
                                postStatus({
                                    status: PostStatus.success,
                                    message: null,
                                })
                            );
                            const { data } = res;
                            if (data) {
                                dispatch(postList(data));
                            } else {
                                dispatch(postList([]));
                            }
                        }, 2000);
                    })
                    .catch((error) => {
                        dispatch(
                            postStatus({
                                status: PostStatus.failed,
                                message: error.message,
                            })
                        );
                    });
            }
        };
    };
    
    <!--自动生成action-->
    export const { postStatus, postList } = postSlice.actions;
    <!--reducer 自动匹配-->
    export default postSlice.reducer;
    
    <!--PostListPage-->
    const PostListPage = (props: Props) => {
        const { history } = props;
        const dispatch = useDispatch();
        const posts: Post_dao[] = useSelector((state: any) => {
            return state.posts.list;
        });
        const status: PostStatus = useSelector((state: any) => {
            return state.posts.status;
        });
    
        useEffect(() => {
            const updateData = () => {
                dispatch(postList_a());
            };
    
            updateData();
        }, [posts, dispatch]);
    
    };
    
    export default PostListPage;
    

    七、总结

    7.1 关于收获

    1. redux 并没有“消灭”组件的局部 state,但是从概念上剥离维护 state 逻辑
    2. react-redux 通过 useSelector 或者 mapStateToProps 剥离(转移/隐藏)绑定组件逻辑
    3. redux-thunk 以中间件的方式弥补 redux 不能异步缺陷
    4. 通过引入新概念(store、reducer、action、slice、thunk)和中间环节(组件绑定、中间件),把状态维护逻辑剥离出去,与组件解耦,使组件更纯粹

    7.2 关于要不要用

    1. 官方不建议无脑使用
    2. 但是还是要无脑使用

    7.3 关于 @reduxjs/toolkit

    1. 用的很方便,但是不利于学习
    2. 直接隐藏了不可变数据处理,与 React 关于不可变数据处理要求“相悖”
    3. 直接屏蔽了 action 对象、生成函数,不利于 redux 3大概念理解

    参考资料

    • why-use-react-redux
    • react-redux-history-implementation
    • 普通用法:connecting-the-components
    • hooks 用法 react-redux-hooks
    • 异步中间件选择:thunks-sagas-observables
    • 调试工具用法 redux-devtools-extension
    • redux-thunk 用法 redux-thunk

    起源地下载网 » redux 学习总结

    常见问题FAQ

    免费下载或者VIP会员专享资源能否直接商用?
    本站所有资源版权均属于原作者所有,这里所提供资源均只能用于参考学习用,请勿直接商用。若由于商用引起版权纠纷,一切责任均由使用者承担。更多说明请参考 VIP介绍。
    提示下载完但解压或打开不了?
    最常见的情况是下载不完整: 可对比下载完压缩包的与网盘上的容量,若小于网盘提示的容量则是这个原因。这是浏览器下载的bug,建议用百度网盘软件或迅雷下载。若排除这种情况,可在对应资源底部留言,或 联络我们.。
    找不到素材资源介绍文章里的示例图片?
    对于PPT,KEY,Mockups,APP,网页模版等类型的素材,文章内用于介绍的图片通常并不包含在对应可供下载素材包内。这些相关商业图片需另外购买,且本站不负责(也没有办法)找到出处。 同样地一些字体文件也是这种情况,但部分素材会在素材包内有一份字体下载链接清单。
    模板不会安装或需要功能定制以及二次开发?
    请QQ联系我们

    发表评论

    还没有评论,快来抢沙发吧!

    如需帝国cms功能定制以及二次开发请联系我们

    联系作者

    请选择支付方式

    ×
    迅虎支付宝
    迅虎微信
    支付宝当面付
    余额支付
    ×
    微信扫码支付 0 元