前端进阶--react全家桶(2)

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 类组件中,你可以使用生命周期方法(如 componentDidMountcomponentDidUpdate)来执行路由拦截逻辑。在函数组件中,可以使用 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 模块,而不是全局样式。这样做的好处有:

  1. 局部作用域: 每个模块的样式只在当前模块内起作用,不会影响其他模块或全局样式。这有助于防止样式的污染和冲突。

  2. 自动哈希命名: 一些构建工具(例如 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后,不需要自己在组件中订阅与取消订阅

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值