1、函数组件的钩子函数React Hooks
1.1、useState(保存与修改组件状态)
1.2、useEffect:模拟创建于销毁功能
1.3、usecallback:缓存
它的主要作用是在渲染过程中缓存一个回调函数,避免在每次渲染时创建新的回调函数,以提高性能。当你将一个回调函数作为 prop 传递给子组件时,可以使用 useCallback
避免不必要的子组件的重新渲染
- 第一个参数是回调函数的具体实现。
- 第二个参数是一个依赖项数组,当依赖项数组中的某个值发生变化时,才会重新生成新的回调函数。如果依赖项数组为空,表示回调函数永远不会改变。
import { useCallback, useEffect, useState } from "react";
export default function TEAT() {
const [num, setnum] = useState(1);
const [name, setName] = useState("lili");
const [show, setShow] = useState(true);
// setTimeout(() => {
// setName(name + 1);
// }, 0);
useEffect(() => {
// setTimeout(() => {
// setnum(num + 1);
// }, 1);
// 会不停滴变化
// setnum(num + 1);
setName(
name.substring(0, 1).toUpperCase() + name.substring(1).toUpperCase()
);
}, [name]);
// 第一次执行一次,后面依赖(函数用到)更新会执行,
const changenum = useCallback((value) => {
setnum(num + value);
}, []);
return (
<>
<div>展示</div>
<div>数字--{num}</div>
<button
onClick={() => {
changenum(2);
}}
>
数字改变2
</button>
<button
onClick={() => {
setName("lisi");
}}
>
改变名字
</button>
<div>名字---{name}</div>
{show && <Child></Child>}
<button
onClick={() => {
setShow(!show);
}}
>
显示与隐藏孩子组件
</button>
</>
);
}
function Child() {
const [num2, setnum2] = useState(1);
const [name2, setName2] = useState("lili");
useEffect(() => {
var timer = setInterval(() => console.log("111"), 100);
return () => {
clearInterval(timer);
};
}, []);
// 销毁钩子,可以使用多个
return (
<>
<div>
<h1>孩子组件{num2}</h1>
</div>
</>
);
}
发送请求
1.4、useMemo缓存结果
useMemo(() => fn, inputs)等价于useCallback(fn, inputs)
- 第一个参数是计算或创建结果的具体实现,将其包装在函数中。
- 第二个参数是一个依赖项数组,当依赖项数组中的某个值发生变化时,才会重新计算结果。如果依赖项数组为空,表示结果永远不会改变。
useMemo(() => fn, inputs)更多用于类似于computed的作用
import React, { useState, useMemo } from 'react';
function ExampleComponent() {
const [count, setCount] = useState(0);
// 使用 useMemo 缓存计算结果
const squaredCount = useMemo(() => {
// 计算结果的具体实现,依赖于 count 的值
return count ** 2;
}, [count]); // 只有在 count 发生变化时才重新计算结果
return (
<div>
<p>Count: {count}</p>
<p>Squared Count: {squaredCount}</p>
<button onClick={() => setCount(count + 1)}>Increment</button>
</div>
);
}
1.5、usecontext
import React, { useContext, useRef, useState } from "react";
var Globalcontext = React.createContext();
export default function Fun(props) {
const [person, setperson] = useState(3);
return (
<Globalcontext.Provider
value={{
family: "liao",
person: person,
change: (num) => {
setperson(num);
},
}}
>
<>
<div>function </div>
<FunTest title="大孩子"></FunTest>
</>
</Globalcontext.Provider>
);
}
function FunTest(props) {
const myref = useRef(null);
const [value, setValue] = useState("输入");
const mynum = useRef(0);
const globalvalue = useContext(Globalcontext);
// return (
// <Globalcontext.Consumer>
// {(value) => {
// value.change(6);
// console.log(value);
// return (
// <>
// <div>我是子组件标题{props.title}</div>
// </>
// );
// }}
// </Globalcontext.Consumer>
// );
return (
<>
<div>我是子组件标题{props.title}</div>
<div>{globalvalue.family}</div>
</>
);
}
1.6、useReducer 共享数据
import React, { useContext, useReducer, useState } from "react";
const reducer = (prevstate, action) => {
console.log(prevstate, action);
let newstate = { ...prevstate };
switch (action.type) {
case "minus":
newstate.count--;
return newstate;
case "add":
newstate.count++;
return newstate;
default:
return prevstate;
}
};
// 状态
const initialstate = {
count: 0,
};
export default function Fun(props) {
const [person, setperson] = useState(3);
// 处理函数
const [state, dispath] = useReducer(reducer, initialstate);
return (
<>
<div>function </div>
<div>
<button
onClick={() => {
dispath({
type: "minus",
});
}}
>
-
</button>
<span>{state.count}</span>
<button
onClick={() => {
dispath({
type: "add",
});
}}
>
+
</button>
</div>
<FunTest title="大孩子"></FunTest>
</>
);
}
function FunTest(props) {
return (
<>
<div>我是子组件标题{props.title}</div>
</>
);
}
1.7、usecontext结合useReducer父子组件通信
import React, { useContext, useReducer, useState } from "react";
// 不支持异步
var Globalcontext = React.createContext();
const reducer = (prevstate, action) => {
console.log(prevstate, action);
let newstate = { ...prevstate };
switch (action.type) {
case "hidden":
newstate.show = !newstate.show;
return newstate;
case "add":
newstate.count++;
return newstate;
default:
return prevstate;
}
};
// 状态,改变子组件状态
const initialstate = {
count: 0,
show: true,
};
export default function Fun(props) {
const [person, setperson] = useState(3);
// 处理函数
const [state, dispath] = useReducer(reducer, initialstate);
return (
<Globalcontext.Provider value={{ state, dispath }}>
<>
<div>function </div>
<div>
<button
onClick={() => {
dispath({
type: "add",
});
}}
>
父亲让孩子+1
</button>
</div>
<Child1></Child1>
{state.show && <Child2></Child2>}
</>
</Globalcontext.Provider>
);
}
function Child1(props) {
const { state } = useContext(Globalcontext);
return (
<>
<div>我是child1111</div>
<h1>{state.count}</h1>
</>
);
}
function Child2(props) {
const { state, dispath } = useContext(Globalcontext);
return (
<>
<div>我是child2222</div>
<h1>{state.show}</h1>
<button
onClick={() => {
dispath({
type: "hidden",
});
}}
>
孩子组件通知父组件隐藏或显示孩子组件
</button>
</>
);
}
1.8、自定义hook
必须以“use”开头
function useFilter(list,value){
let reslist=useMemo(()=>{
list.map((item) => item.name.inludes(value));
},[list,value])
return { reslist }
}
2、路由
以前也是单页面开发,但是没有实现页面与url地址的映射
在 React 中,通常使用第三方库来处理路由。最常见的路由库之一是 React Router。React Router 提供了一组用于在 React 应用中处理导航和路由的组件。
2.1、安装 React Router:
npm install react-router-dom
2.2、使用 BrowserRouter:
在应用的最顶层,使用 BrowserRouter
组件包裹你的组件。它是 React Router 的根组件,用于提供路由上下文。
import { BrowserRouter as Router } from 'react-router-dom';
function App() {
return (
<Router>
{/* Your components and routes go here */}
</Router>
);
}
2.3、定义路由和组件:
普通路由
使用 Route
组件定义路由,并指定对应的组件。
路由动态参数
// 定义带有路由参数的路由
<Route path="/user/:id" component={UserProfile} />
// 在组件中访问路由参数
function UserProfile(props) {
const userId = props.match.params.id;
// 根据 userId 做一些事情
return <div>User Profile Page for ID: {userId}</div>;
}
navigate('/about/123?name=John');
{`/about/${id}'}
{'/about/'+id}
不建议下面
navigate({ path: '/about/123', query: { name: 'John' } });
import { useParams, useLocation } from 'react-router-dom';
const { id } = useParams();
const location = useLocation();
const searchParams = new URLSearchParams(location.search);
const name = searchParams.get('name');
动态路由
import React from 'react';
import { BrowserRouter as Router, Route, Link } from 'react-router-dom';
const projects = [
{ id: 'project1', name: 'Project 1' },
{ id: 'project2', name: 'Project 2' },
// ...
];
function ProjectDetails(props) {
const projectId = props.match.params.id;
const project = projects.find((p) => p.id === projectId);
if (!project) {
return <div>Project not found</div>;
}
return (
<div>
<h2>{project.name}</h2>
{/* Display project details */}
</div>
);
}
function App() {
return (
<Router>
<div>
<ul>
{/* Create navigation links for each project */}
{projects.map((project) => (
<li key={project.id}>
<Link to={`/projects/${project.id}`}>{project.name}</Link>
</li>
))}
</ul>
{/* Define dynamic routes for each project */}
{projects.map((project) => (
<Route
key={project.id}
path={`/projects/${project.id}`}
component={ProjectDetails}
/>
))}
</div>
</Router>
);
}
export default App;
在上述例子中,/projects/:id
路由是根据数据动态生成的,每个项目都会有一个对应的路由。ProjectDetails
组件可以通过 props.match.params.id
访问项目的 ID。
2.4、使用 Link与useNavigate
实现导航:
确保你的项目已经安装了 React Router 版本 6,npm install react-router-dom@next
在函数组件中使用 useNavigate
钩子进行路由导航。
<Route path="about" element={<About />}>
<Route index element={<AboutHome />} />
<Route path="team" element={<Team />} />
<Route path="contact" element={<Contact />} />
</Route>
import { Link, Outlet, useLocation ,useNavigate} from "react-router-dom";
function About() {
const location = useLocation();
const navigate = useNavigate();
const handleButtonClick = () => {
// 使用 navigate 函数进行路由跳转
navigate('/other-route');
};
return (
<div>
<h2>About</h2>
<Link to="team" className={location.pathname === "/films/team" ? "activeClassName" : ""}>team</Link>
<Link to="contact" className={ location.pathname === "/films/contact" ? "activeClassName" : "" } >
contact
</Link>
<button onClick={handleButtonClick}>跳转到其他路由</button>
<Outlet /> {/* 嵌套路由的出口 */}
</div>
);
}
在类组件中
import React from 'react';
import { useNavigate } from 'react-router-dom';
class MyClassComponent extends React.Component {
handleButtonClick = () => {
// 获取 useNavigate 函数进行路由导航
const navigate = useNavigate();
navigate('/new-route');
};
render() {
return (
<div>
<p>This is my class component.</p>
<button onClick={this.handleButtonClick}>Go to New Route</button>
</div>
);
}
}
export default MyClassComponent;
2.5、路由重定向
React Router 提供了 <Redirect>
组件来实现路由重定向。在上述例子中,如果用户访问了未定义的路径,将渲染 <Route component={NotFound} />
,显示“404 Not Found”
import React from 'react';
import { BrowserRouter as Router, Route, Redirect, Switch } from 'react-router-dom';
function App() {
return (
<Router>
<Switch>
<Route path="/home" component={Home} />
<Route path="/about" component={About} />
<Redirect from="/" to="/home" />
<Route component={NotFound} />
</Switch>
</Router>
);
}
export default App;
2.6、路由嵌套
路由嵌套允许你在应用程序中创建多层次的路由结构。父级路由中的组件可以包含子级路由,子级路由有自己的路径和组件。
import React from 'react';
import { BrowserRouter as Router, Route, Switch } from 'react-router-dom';
function App() {
return (
<Router>
<Switch>
<Route path="/home" component={Home} />
<Route path="/about" component={About} />
<Route path="/dashboard" component={Dashboard} />
</Switch>
</Router>
);
}
function Dashboard() {
return (
<div>
<h2>Dashboard</h2>
<Switch>
<Route path="/dashboard/profile" component={Profile} />
<Route path="/dashboard/settings" component={Settings} />
</Switch>
</div>
);
}
function Profile() {
return <div>User Profile</div>;
}
function Settings() {
return <div>Settings</div>;
}
function Home() {
return <div>Home Page</div>;
}
function About() {
return <div>About Page</div>;
}
export default App;
2.7、路由守卫
在 React Router 中,路由守卫是一种通过拦截导航事件来进行一些操作的机制。路由守卫指的是在导航到某个路由之前或之后执行一些操作的过程。
(1)<Route>
组件的 render
属性:
使用 <Route>
组件的 render
属性,你可以在路由匹配时执行一些操作,例如身份验证、权限检查等。以下是一个简单的示例:
import React from 'react';
import { Route, Redirect } from 'react-router-dom';
const PrivateRoute = ({ component: Component, ...rest }) => {
const isAuthenticated = /* 根据实际情况获取用户身份验证状态 */ true;
return (
<Route
{...rest}
render={(props) =>
isAuthenticated ? <Component {...props} /> : <Redirect to="/login" />
}
/>
);
};
export default PrivateRoute;
import React from 'react';
import { BrowserRouter as Router, Route, Switch } from 'react-router-dom';
import PrivateRoute from './PrivateRoute';
import Home from './Home';
import Dashboard from './Dashboard';
import Profile from './Profile';
import Settings from './Settings';
import Login from './Login';
function App() {
return (
<Router>
<Switch>
<Route path="/login" component={Login} />
<PrivateRoute path="/dashboard" component={Dashboard} />
<Route path="/" component={Home} />
</Switch>
</Router>
);
}
export default App;
PrivateRoute
组件被用于包装 /dashboard
路由,要求用户登录后才能访问。Dashboard
组件中还包含了两个子路由:/dashboard/profile
和 /dashboard/settings
,它们可以继续使用 PrivateRoute
进行身份验证
(2)具体使用
参考:React Router V6 官方路由身份验证示例 - 掘金 (juejin.cn)
首次,状态为false,登录后,改变状态
if (!auth.user) {
// 未登入,使用Navigate组件重定向到登录页,传入state属性以保存当前URL位置信息
return <Navigate to="/login" state={{ from: location }} replace />;
}
(3)路由拦截
React Router 并没有内置专门的拦截器,但可以通过以下方法实现路由拦截:在 React 类组件中,你可以使用生命周期方法(如 componentDidMount
、componentDidUpdate
)来执行路由拦截逻辑。在函数组件中,可以使用 useEffect
钩子来执行路由拦截逻辑。
2.8、路由懒加载
使用 React.lazy()
和 import()
:
import React, { lazy } from 'react';
import { BrowserRouter as Router, Routes, Route } from 'react-router-dom';
const Home = lazy(() => import('./Home'));
const About = lazy(() => import('./About'));
const App = () => (
<Router>
<Routes>
<Route path="/home" element={<React.Suspense fallback={<div>Loading...</div>}><Home /></React.Suspense>} />
<Route path="/about" element={<React.Suspense fallback={<div>Loading...</div>}><About /></React.Suspense>} />
</Routes>
</Router>
);
export default App;
做简单的封装
3、CSS 模块化
css会默认所有的样式插入到head中,会引起污染,所以css的文件夹名字为A.module.css
CSS 文件将被视为一个 CSS 模块,而不是全局样式。这样做的好处有:
-
局部作用域: 每个模块的样式只在当前模块内起作用,不会影响其他模块或全局样式。这有助于防止样式的污染和冲突。
-
自动哈希命名: 一些构建工具(例如 webpack)在构建时会自动为 CSS 类名生成唯一的哈希值,以确保在全局范围内唯一性。
import stlye from "./css/test.module.css";
<Link to="team"
className={
location.pathname === "/films/team" ? stlye.activeClassName : ""
}
>
team
</Link>
4、 Ant Design
包含很多设计原则和配套的组件库。
import { Button, Flex } from "antd";
5、类组件的状态维护(现在一般用函数组件,可以忽略)
5.1、浅复制
obj1=obj2 只是把obj2的地址交给obj1而已,修改obj1会影响obj2
obj1={...obj2} 只是把obj2深一层的地址交给obj1而已,修改obj1深一层的数组或对象会影响obj2
json.parse(json.stringfy())可以实现深拷贝,但是前提不能有undefine,NAN,null
deepcopy性能不好,需要遍历
5.2、Immutable
每次修改一个 Immutable 对象时都会创建一个新的不可变的对象,在新对象上操作并不会影响到原对象的数据
fromJS (x) x.toJS() setIn() undateIn([],()=>{})
import { fromJS } from "immutable";
class Test extends Component {
state = {
info: fromJS({
name: "lili",
address: {
provice: "GD",
city: "zhanjiang",
},
family: ["yi", "er", "san"],
}),
};
render() {
return (
<>
<h1>我的信息--{this.state.info.get("name")}</h1>
<h1>
我的信息--{this.state.info.get("address").get("city")}
<button
onClick={() => {
this.setState({
info: this.state.info.setIn(["address", "city"], "guangzhou"),
});
}}
>
修改
</button>
</h1>
<ul>
{this.state.info.get("family").map((item, index) => (
<li key={item}>
{item}
<button
onClick={() => {
console.log("111");
this.setState({
info: this.state.info.updateIn(["family"], (list) =>
list.splice(index, 1)
),
});
}}
>
del
</button>
</li>
))}
</ul>
</>
);
}
5.3、 mobx
autorun监听数据改变,就会执行回调函数
组件内部自己更新store数据,不统一,不利于管理。需要引进action
也可以模仿官网使用面向对象的写法,使用装饰器,但是需要自己配置vscode与项目支持装饰器语法
mobx的异步函数需要特殊处理。例如请求回来的数据,修改状态,需要使用runInAction
有一个问题,当时常来回切换页面时,会创建多个监听器。(因为每一次更新都会创建一次函数),需要在销毁组件前,取消订阅
mobx-react(项目使用???为什么不直接使用mobx就好了,这个好麻烦???)
创建了仓库store.js后,不需要自己在组件中订阅与取消订阅