开启学习react+ts,本篇主要是学习使用React Redux和项目的路由配置
一、React Redux
需求:使用TS+React Redux实现一个累加。
A. 安装
$ yarn add redux react-redux redux-devtools-extension
B. Store
src下新建 store 目录,在其中新建 reducer.ts 和 index.ts :
a. reducer
const defaultState = {
num: 1
}
interface IAction {
type: string;
value: number;
}
export default (state=defaultState, action: IAction) => {
let newState = JSON.parse(JSON.stringify(state));
switch(action.type){
case "increase":
newState.num+=action.value;
break;
default:
break;
}
return newState;
}
b. store
import {applyMiddleware, createStore} from 'redux'
import reducer from './reducer'
import {composeWithDevTools} from 'redux-devtools-extension'
const store = createStore(reducer, composeWithDevTools(applyMiddleware()))
export default store;
C. Provider
在入口文件 index.tsx 中:
import ReactDOM from 'react-dom'
import App from './App4'
import {Provider} from 'react-redux'
import store from 'store'
ReactDOM.render(
<Provider store={store}>
<App />
</Provider>,
document.getElementById("root")
)
D. connect
在组件中:
import {connect} from 'react-redux'
import React from 'react'
import {Dispatch} from 'redux'
interface IProps {
num: number;
increaseFn: ()=>void
}
const App4: React.FC<IProps> = (props) => {
return (
<div>
<h3>{props.num}</h3>
<button onClick={()=>props.increaseFn()}>累加</button>
</div>
)
}
const mapStateToProps = (state: {num: number}) => {
return {
num: state.num
}
}
const mapDispatchToProps = (dispatch: Dispatch) => {
return {
increaseFn(){
dispatch({type: "increase", value: 2})
}
}
}
export default connect(mapStateToProps, mapDispatchToProps)(App4)
五、路由
1、安装路由
本课程使用目前最新版的React路由(v6) (opens new window):
$ npm install react-router-dom@6
# 或者
$ yarn add react-router-dom@6
注意:
版本会随时更新,因此请指定版本安装。
2、路由配置
a. 路由创建
在 src 下创建 router>index.tsx 。以首页与登录页切换为例:
import App from "App6";
import Home from "Home";
import List from "List";
import Detail from "Detail";
import About from "About";
import Login from "Login";
import { BrowserRouter as Router, Routes, Route } from "react-router-dom";
const MyRouter = () => (
<Router>
<Routes>
<Route path="/" element={<App />}>
<Route index element={<Home />}></Route>
<Route path="/list" element={<List />}></Route>
<Route path="/detail" element={<Detail />}></Route>
<Route path="/about" element={<About />}></Route>
</Route>
<Route path="/login" element={<Login />}></Route>
</Routes>
</Router>
);
export default MyRouter;
关键词解析:
1、BrowserRouter重命名为Router
2、所有的Route组件必须放在Routes组件中
3、Route标签上的element属性必须填写标签结构的组件,如:,而不是 Home
4、加了index属性的路由不需要写path,因为/路径就指向该组件
b. 入口文件引入路由
src>index.tsx :
import ReactDOM from 'react-dom'
import MyRouter from 'router'
ReactDOM.render(
<MyRouter />,
document.getElementById("root")
)
c. 组件显示
App.tsx 中:
import React from "react";
import { Outlet, Link } from "react-router-dom";
function App() {
return (
<div>
<ul>
<li><Link to={"/list"}>列表页</Link></li>
<li><Link to={"/detail"}>详情页</Link></li>
<li><Link to={"/about"}>关于我们</Link></li>
</ul>
<Outlet />
</div>
);
}
export default App;
关键词解析:
1、 组件用来显示子路由,类似于Vue的
2、Link最终会被html解析为a标签
目前结合ts的情况下,无法使用index属性指定首页组件,因此如果希望 / 跳转 /home ,需要:
import { useLocation } from "react-router-dom";
let { pathname } = useLocation();
useEffect(() => {
if (pathname === "/") {
navigate("/home");
}
}, []);
3、参数获取
a. 子路由形式携带
路由跳转往往伴随着参数的传递,假如:
<Route path="/login/:id" element={<Login />}></Route>
<Link to="/login/123">登录页</Link>
此时可以使用React Router Dom提供的Hook来获取:
import { useParams } from 'react-router-dom'
const {id} = useParams()
console.log(id)
b. 问号(?)形式参数
<Route path="/login" element={<Login />}></Route>
<Link to="/login?id=123">登录页</Link>
获取形式:
import { useSearchParams } from 'react-router-dom'
const [params] = useSearchParams()
console.log(params.getAll('id'))
以上的id其实属于携带方式不明确,也不一定会携带,因此路由可以设置为:
<Route path="/login/*" element={<Login />}></Route>
4、事件跳转
事件中执行跳转页面,可以使用useNavigate这个hook进行跳转。
import { useNavigate } from "react-router-dom";
const navigate = useNavigate();
const goLogin = () => {
navigate('/login')
}
<span onClick={goLogin}>登录页2</span>
简单参数的传递可以直接带在url后,而复杂参数需要以复杂数据类型的形式携带:
const navigate = useNavigate();
navigate('/login', {state: {id: 456}})
注意:
navigate方法第二个参数必须是对象,而且这个对象只接受replace和state两个属性,state可以用来携带参数。
携带复杂参数,可以使用useLocation来获取参数:
const location = useLocation()
console.log(location.state.id);
注意:
这里如果使用了TS,那么location会报错,因为其中的state属于不确定的类型,因此没办法直接location.state调用。解决方法有两个:一是单独设置state字段为any,二是直接设置location类型为any。
interface ILocation {
state: any,
search: string,
pathname: string,
key: string,
hash: string
}
const location: ILocation = useLocation()
const location: any = useLocation()
5、404匹配
当路由为404时,可以对路由文件 router/index.tsx 进行如下匹配:
...
import NoMatch from "NoMatch";
import { BrowserRouter as Router, Routes, Route } from "react-router-dom";
const MyRouter = () => (
<Router>
<Routes>
<Route path="/" element={<App />}>
...
</Route>
<Route path="/login" element={<Login />}></Route>
<Route path="*" element={<NoMatch />}></Route>
</Routes>
</Router>
);
export default MyRouter;
如此,输入错误路径,就会自动重定向到404页面了。
|