umi4的资料实在太少踩坑很多,希望可以帮助到你
1、安装 dva,react-redux
yarn add dva@latest -s
yarn add react-redux@latest -s
2、在src下 新建dva.js创建dva 实例
import { create } from 'dva-core';
// import { createLogger } from 'redux-logger';
import createLoading from 'dva-loading';
import { message } from 'antd';
// 1. 创建 dva 实例
const app = create({
onError(e) {
message.error(e.message, 3);
},
});
// 2. 使用插件
// app.use(createLogger());
app.use(createLoading());
// 3. 注册模型
// app.model(require('./models/counter').default);
// 4. 导出 dva 实例
export default app;
3、在src下 新建models文件夹并在其下新建counter.ts
export default {
namespace: "counter",
state: {
count: 0,
token: "",
},
reducers: {
add(state: {count: number}) {
return {count: state.count + 1};
},
minus(state: {count: number}) {
return {count: state.count - 1};
},
setToken(state: any, { payload: token }: any) {
console.log(token,'storn')
return {...state, token};
},
},
effects: {},
subscriptions: {},
};
4、在app.tsx中注入dva.js实例
import React from "react";
import {Provider} from "react-redux";
import app from "./dva";
// 初始化 dva
app.start();
export function render(oldRender: () => void) {
oldRender();
}
export function rootContainer(
container:
| string
| number
| boolean
| React.ReactElement<any, string | React.JSXElementConstructor<any>>
| React.ReactFragment
| React.ReactPortal
| null
| undefined
) {
return React.createElement(
React.createElement(Provider, {store: app._store, children: container})
);
}
5、使用案例一、
import {connect} from "dva";
interface CounterProps {
count: number;
dispatch: Function;
}
const Counter: React.FC<CounterProps> = ({count, dispatch}) => (
<div>
<h2>{count}</h2>
<button onClick={() => dispatch({type: "counter/add"})}>+</button>
<button onClick={() => dispatch({type: "counter/minus"})}>-</button>
</div>
);
export default connect(({counter}) => counter)(Counter);
案例二,存储token
import {MenuFoldOutlined, MenuUnfoldOutlined} from "@ant-design/icons";
import {ProLayout} from "@ant-design/pro-layout";
import {connect} from "dva";
import {JSXElementConstructor, ReactElement, ReactFragment, ReactPortal, useEffect, useState} from "react";
import {history, Link, Outlet, useLocation, useSelectedRoutes} from "umi";
import proSettings from "./components/defaultSettings";
import setRightContentRender from "./components/rightContentRender";
import style from "./index.less";
import {handleRedirect} from "./msalConfig";
import defaultProps from "./_defaultProps";
interface CounterProps {
dispatch: Function;
}
const BasicLayout: React.FC<CounterProps> = ({dispatch}) => {
const [selectedKeys, setSelectedKeys] = useState<string[]>([]);
// eslint-disable-next-line @typescript-eslint/no-unused-vars
const [pathname, setPathname] = useState<any>("/login");
const [collapsed, setCollapsed] = useState<boolean | undefined>(false);
const routes = useSelectedRoutes();
const lastRoute = routes.at(-1);
// useEffect(() => {
// history.push(pathname);
// }, [pathname]);
useEffect(() => {
handleRedirect().then(res => {
if (res) {
history.push("/home");
dispatch({type: "counter/setToken", payload: res?.accessToken});
console.log(res?.accessToken);
}
});
}, []);
// 自定义collapsed
const CustomCollapsedButton = (val: boolean | undefined) => {
const toggleCollapsed = () => {
setCollapsed(!collapsed);
};
return val ? (
<MenuUnfoldOutlined onClick={toggleCollapsed} className={style.collapsed} />
) : (
<MenuFoldOutlined onClick={toggleCollapsed} className={style.collapsed} />
);
};
const handleMenuSelect = ({key}: {key: string}) => {
setSelectedKeys([key]);
};
// 将topmenu转为side
interface TopMenu {
type: "topmenu";
}
function convertTopMenuToTop(layout: TopMenu) {
return {...layout, type: "side"};
}
const topMenu: TopMenu = {type: "topmenu"};
const layout: any = proSettings.layout === "side" ? convertTopMenuToTop(topMenu) : proSettings.layout;
if (lastRoute?.pathname === "/login") {
return (
<div>
<Outlet />
</div>
);
} else {
return (
<div>
<ProLayout
navTheme='light'
style={{minHeight: "100vh"}}
contentStyle={{margin: 0}}
disableMobile // 是否禁用移动端模式
layout={layout} // topmenu | mix | top
fixSiderbar
collapsed={collapsed}
collapsedButtonRender={(val: boolean | undefined) => CustomCollapsedButton(val)} // 自定义collapsed 菜单展开/收起icon
title='JIM'
logo={
<img
onClick={() => {
history.push("/home");
}}
className={style.img}
src='https://gw.alipayobjects.com/zos/antfincdn/aPkFc8Sj7n/method-draw-image.svg'
alt=''
/>
}
// 菜单
{...defaultProps}
location={{
pathname,
}}
// 切换菜单
menuItemRender={(
menuItemProps: {isUrl: any; children: any; path: any},
defaultDom:
| string
| number
| boolean
| ReactElement<any, string | JSXElementConstructor<any>>
| ReactFragment
| ReactPortal
| null
| undefined
) => {
if (menuItemProps.isUrl || menuItemProps.children || !menuItemProps.path) {
return defaultDom;
}
const isSelected = menuItemProps.path === lastRoute?.pathname; // 判断当前菜单项是否被选中
const style = isSelected ? {color: "green"} : {}; // 根据选中状态动态添加样式
return (
<Link to={menuItemProps.path} style={style}>
{defaultDom}
</Link>
);
}}
selectedKeys={selectedKeys}
onSelect={handleMenuSelect}
rightContentRender={setRightContentRender}
>
<div className={style.navs}>
<Outlet />
</div>
{/* <div className={style.set}>设置</div> */}
</ProLayout>
</div>
);
}
};
export default connect(({counter}) => counter)(BasicLayout);