前言
最近学习了慕课上 Vue3 移动端类似点单 App 的编码,想着用 ReactV6 版本也可以实现,模拟接口使用的是FastMock,于是开始了踩坑填坑之旅。首先粘一下两个版本库地址,有兴趣的小伙伴可以在 github 上 star✨ 哦。
原型效果
目录结构
踩坑问题点及解决记录
1、样式穿透
在开发的过程中发现,React 如果定义了相同的类名,样式会相互影响。所以要么定义不同的类名,要么采用其他的方式。我是使用了styled-components
这个 npm 库,使用方式:
import styled from "styled-components";
const Wrapper = styled.div`
overflow-y: scroll;
position: absolute;
top: 0;
bottom: 0.5rem;
left: 0;
right: 0;
background: rgb(248, 248, 248);
`;
function ComponentDemo() {
return <Wrapper>我是具有样式的div标签</Wrapper>;
}
export default ComponentDemo;
2、路由跳转
因为使用的 React 版本是 V6,所以与老版本的一些方法还是有一些区别的。可以随意跳转到其他页面,使用了react-router-dom
库里面的 useNavigate 方法,使用方式:
import { useNavigate } from "react-router-dom";
function ComponentDemo() {
const navigate = useNavigate();
const handleRouterClick = () => {
navigate("/home");
};
return <div onClick={handleRouterClick}>点我跳转到首页</div>;
}
export default ComponentDemo;
3、动态样式
动态样式使用了classnames
库,使用方式:
import classnames from "classnames";
function ComponentDemo() {
const isDynamicClass = true;
return (
<div className={classnames("class__a", { class__b: isDynamicClass })}>
我有动态样式 class__b
</div>
);
}
export default ComponentDemo;
4、登录重定向
因为登录状态是使用的 localStorage
,所以可以根据设置的状态值进行判断,例如在登录页面里面线获取localStorage.isLogin
,如果 isLogin 为 true,则代表已经登陆,那么访问 login 就需要跳转到首页(/home),使用方式如下:
import { Navigate } from "react-router-dom";
function ComponentDemo() {
const isLogin = localStorage.isLogin;
return (
<div>
{isLogin && <Navigate to={"/home"} replace />} {/*我要跳转到首页*/}
{!isLogin && <div>我是登录页</div>}
</div>
);
}
export default ComponentDemo;
5、整体路由配置
router 下的 index.js
import Home from "../views/home/Home";
import CartList from "../views/cartList/CartList";
import OrderList from "../views/orderList/OrderList";
import OwnPage from "../views/ownPage/OwnPage";
import OrderConfirmation from "../views/orderConfirmation/OrderConfirmation";
import Shop from "../views/shop/Shop";
import Login from "../views/login/Login";
import Register from "../views/register/Register";
const router = [
{
path: "/",
element: <Home />,
exact: true,
},
{
path: "/cartList",
element: <CartList />,
exact: true,
},
{
path: "/orderList",
element: <OrderList />,
exact: true,
},
{
path: "/ownPage",
element: <OwnPage />,
exact: true,
},
{
path: "/orderConfirmation/:id",
element: <OrderConfirmation />,
exact: true,
},
{
path: "/shop/:id",
element: <Shop />,
exact: true,
},
{
path: "/login",
element: <Login />,
exact: true,
},
{
path: "/register",
element: <Register />,
exact: true,
},
];
export default router;
主入口 index.js
import React from "react";
import ReactDOM from "react-dom/client";
import reportWebVitals from "./reportWebVitals";
import { BrowserRouter as Router, useRoutes } from "react-router-dom";
import router from "./router/index";
import "./style/index.scss";
import { Provider } from "react-redux";
import store from "./store/index";
const GetRoutes = () => {
return useRoutes(router);
};
const SetRoutes = () => {
return (
<Router>
<GetRoutes />
</Router>
);
};
ReactDOM.createRoot(document.getElementById("root")).render(
// 根组件配置:Provider声明式开发,提供给子组件数据管理功能
<Provider store={store}>
<SetRoutes />
</Provider>
);
/*
* 如果你想开始测量你的应用程序的性能,传递一个函数
* 记录结果(例如:reportWebVitals(console.log))
* 或发送到分析端点。 了解更多:https://bit.ly/CRA-vitals
*/
reportWebVitals();
6、数据管理 Redux 配置
store 文件夹下配置:
index.js
/**
* 主仓库配置
*/
import { createStore, compose, applyMiddleware } from "redux";
import thunk from "redux-thunk";
import reducer from "./reducer";
const composeEnhancers = window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__ || compose; // // 引入Redux可视化插件
const store = createStore(
reducer, // 仓库数据
composeEnhancers(
// 组合中间件
applyMiddleware(thunk) // 异步用中间件
)
);
export default store;
reducer.js
import { combineReducers } from "redux";
import { reducer as CartListReducer } from "../views/shop/store/index";
export default combineReducers({
cartList: CartListReducer,
});
因为功能并不复杂,全局只配置了一个购物车数据,并对他进行操作。在相对应的文件夹下建立 store,并配置对应的方法,以下是实现的方式:在 shop 文件夹下新建
- actionCreator.js:负责统一管理数据状态改变的函数执行,给 reducer 分配相应的 action
import * as actionTypes from "./constants";
export const changeCartItemInfo = (data) => ({
type: actionTypes.CHANGE_CART_ITEM_INFO,
data,
});
export const changeCartItemCheckInfo = (data) => ({
type: actionTypes.CHANGE_CART_ITEM_CHECK_INFO,
data,
});
export const setCartItemCheckedInfo = (data) => ({
type: actionTypes.SET_CART_ITEM_CHECKED,
data,
});
export const cleanCartProductInfo = (data) => ({
type: actionTypes.CLEAN_CART_PRODUCT,
data,
});
export const clearCartDataInfo = (data) => ({
type: actionTypes.CLEAR_CART_DATA_INFO,
data,
});
export const changeShopNameInfo = (data) => ({
type: actionTypes.CHANGE_SHOP_NAME,
data,
});
- constants.js:定义的常量
export const CHANGE_CART_ITEM_INFO = "CHANGE_CART_ITEM_INFO";
export const CHANGE_CART_ITEM_CHECK_INFO = "CHANGE_CART_ITEM_CHECK_INFO";
export const CLEAN_CART_PRODUCT = "CLEAN_CART_PRODUCT";
export const SET_CART_ITEM_CHECKED = "SET_CART_ITEM_CHECKED";
export const CHANGE_SHOP_NAME = "CHANGE_SHOP_NAME";
export const CLEAR_CART_DATA_INFO = "CLEAR_CART_DATA_INFO";
- reducers.js:负责根据 action 值,做相应操作,以实现数据流管理
import * as actionTypes from "./constants";
const setLocalCartList = (state) => {
const { cartList } = state;
const cartListString = JSON.stringify(cartList);
localStorage.cartList = cartListString;
};
const getLocalCartList = () => {
try {
return JSON.parse(localStorage.cartList);
} catch {
return {};
}
};
const defaultState = {
cartList: getLocalCartList(),
};
const reducers = (state = defaultState, action) => {
switch (action.type) {
case actionTypes.CHANGE_CART_ITEM_INFO:
// 执行一些操作
return {
...state,
};
case actionTypes.CHANGE_CART_ITEM_CHECK_INFO:
// 执行一些操作
return { ...state };
case actionTypes.CHANGE_SHOP_NAME:
// 执行一些操作
return { ...state };
case actionTypes.CLEAR_CART_DATA_INFO:
// 执行一些操作
return { ...state };
default:
return { ...state };
}
};
export default reducers;
- index.js:负责整理仓库功能,并统一向外输出
import reducer from "./reducers";
import * as actionCreators from "./actionCreator";
import * as constants from "./constants";
export { reducer, actionCreators, constants };
在组件里面使用:
import { memo } from "react";
import { connect } from "react-redux";
import { changeCartItemInfo } from "./store/actionCreator";
function ComponentDemo(props) {
const { changeCartItemInfoDispatch } = props;
const changeCartItem = (data) => {
changeCartItemInfoDispatch(data);
};
return <div onClick={() => changeCartItem(data)}>我是具有样式的div标签</div>;
}
const mapStateToProps = (state) => {
return { ...state };
};
const mapDispatchToProps = (dispatch) => {
return {
changeCartItemInfoDispatch(state) {
dispatch(changeCartItemInfo(state));
},
};
};
export default connect(
mapStateToProps,
mapDispatchToProps
)(memo(ComponentDemo));
7、React 内置方法使用
7.1 useEffect
import { useEffect, useState } from "react";
import { get } from "../../utils/request";
function ComponentDemo() {
const [data, setData] = useState({
currentTab: "all",
});
useEffect(() => {
setData({ shopId: location.pathname.split("/")[2] });
const getContentData = async () => {
const res = await get(`/api/shop/${data.shopId}/products`, {
tab: data.currentTab,
});
if (res?.errno && res?.data) {
setData({
list: res.data.filter((item) => item.tab === data.currentTab),
});
}
};
getContentData();
}, [data.currentTab]);
return <div>{data}</div>;
}
export default ComponentDemo;
7.2 useState
在使用过程中发现,useState 里面可能会定义多个变量,如果只想更新其中的一个或者两个变量,使用 useState 会变得不够方便,所以 utils 文件夹下添加了 useMergeState.js 文件,可以修改其中一个变量但是不影响其余变量的使用:
import { useState } from "react";
const useMergeState = (initialState) => {
const [state, setState] = useState(initialState);
const setMergeState = (newState) =>
setState((prevState) => ({ ...prevState, ...newState }));
return [state, setMergeState];
};
export default useMergeState;
使用 setMergeState 和使用 useState 类似。
最后,以上是在使用过程中会出现卡壳的情况,分享一下,希望有点帮助。有问题可以评论哦。