react学习

一、创建react项目

1. vite创建react项目
 npm create vite@latest
2. 安装react-router-dom路由
npm i react-router-dom
3. 安装状态管理库

这里可以使用redux,但是个人觉得比较繁琐,于是使用的是一个类似于vue3中的pinia的一个库valtio

npm i valtio
4. 安装ui库

这里使用Ant Design,但是这个库有点小问题,那就是样式中的px不能被postcss-pxtorem插件转换成rem

npm install antd --save
5. 安装axios
npm i axios

二、react-router-dom使用

1. 路由文件
// router.js
import { createHashRouter, Navigate } from "react-router-dom";
import Home from "@/views/Home";
import HomePage from "@/views/home/HomePage";
import ThreeDimensional from "@/views/home/ThreeDimensional";
import AuthRoute from "@/views/AuthRoute";
import Login from "@/views/Login";
const router = createHashRouter([
  {
    path: "/",
    element: <AuthRoute />,
    children: [
      {
        path: "home",
        element: <Home />,
        children: [
          {
            path: "homePage",
            element: <HomePage />,
          },
          {
            path: "ThreeDimensional",
            element: <ThreeDimensional />,
          },
          {
            path: "",
            element: <Navigate to="homePage"></Navigate>,
          },
        ],
      },
      {
        path: "login",
        element: <Login />,
      },
      {
        path: "",
        element: <Navigate to="home"></Navigate>,
      },
    ],
  },
]);
export default router;

AuthRoute页面中实现全局路由守卫,这个页面是/对应的页面

// AuthRoute.tsx
import { useEffect } from 'react';
import { Outlet, useLocation,useNavigate} from 'react-router-dom';
const AuthRoute = () => {
  const location = useLocation();
  const navigate = useNavigate();
  useEffect(() => {
    // 路由拦截处理逻辑
    let token = localStorage.getItem('token');
    if(!token){  //无token进入登录页
        navigate("/login");
    }else{
        if(location.pathname === "/login"){  //有token不能跳转登录页,直接去首页
            navigate("/")
        }
    }
  }, [location.pathname]);
  return <Outlet />;
};
export default AuthRoute;

App.tsx做项目的入口

import { RouterProvider } from "react-router-dom";
import router from "./router";

function App() {
  return (
    <>
      <RouterProvider router={router}></RouterProvider>
    </>
  );
}

export default App;

遗留问题:由于目前还不是很熟悉react这一套,有以下问题需要后期解决

  • 如何实现类似于vue-router里面的meta属性来给每个路由自定义参数
  • 在路由跳转后,如何获取之前的路由地址
  • 如何实现可暂停的路由跳转功能,类似于vue-router中的路由守卫中间件,执行next方法才会跳转页面

三、valtio使用

// index.tsx
import { proxy } from "valtio";
export const gasListState:IGasListState= proxy({
  gasList: [],
});
export function setGasList(list:IGasList[]) {
  gasListState.gasList = list;
}

使用方法

import { useSnapshot } from "valtio";
import { gasListState } from "@/store/gasList";
let gastate = useSnapshot(gasListState);  //通过useSnapshot方法包裹gasListState

四、hook使用

1. useCallback

useCallback是一个允许你在多次渲染中缓存函数的 React Hook

//使用方式
const cachedFn = useCallback(fn, dependencies)

它会返回传入的fn,如果dependencies依赖项没有发生变化的时候,组件多次渲染也是返回缓存的fn,也就是说,cachedFn的值是同一个值,注意它只是返回fn,并不是调用fn,需要手动调用cachedFn()

import { useState, useCallback, useEffect, useMemo } from "react";

export default function UseCallbackExample() {
  console.log("我渲染了");
  const [num, setNum] = useState(1);
  // 使用 useCallback 来创建一个优化过的函数
  const getDoubleNum = useCallback(() => {
    console.log("获取双倍的 num");
    return 2 * num;
  }, []);
  // const getDoubleNum = () => {
  //   console.log('获取双倍的 num');
  //   return 2 * num;
  // };
  // 模拟外部处理逻辑
  useEffect(() => {
    console.log("外部处理逻辑");
  }, [getDoubleNum]);

  return (
    <div>
      <p>当前数字: {num}</p>
      <button onClick={() => setNum(num + 1)}>增加数字</button>
    </div>
  );
}

上面的代码,点击按钮会发现并没有打印useEffect里面的“外部处理逻辑”,因为getDoubleNum已经被缓存了,组件每次渲染getDoubleNum都是同一个值,如果使用被注释的普通函数,就会发现每次都打印了“外部处理逻辑”

2. useMemo

vue中的计算属性,不多说

import { useState,useMemo } from "react";

export default function UseCallbackExample() {
  const [num, setNum] = useState(1);
  const [count, setCount] = useState(1);
  const expensiveCalculation = useMemo(() => {
    console.log("执行昂贵的计算...");
    return count * 1000000;
  }, [count]);
  return (
    <div>
      <p>当前计算值: {expensiveCalculation}</p>
      <button onClick={() => setNum(num + 1)}>增加数字</button>
    </div>
  );
}
3. useContext

useContext是一个 React Hook,可以让你读取和订阅组件中的context,主要用于组件通信,避免组件层级太深,一层一层往下传递的尴尬

import React, { createContext, useContext, useState } from "react";

// 创建一个 Context
const ThemeContext = createContext({});

function ThemeProvider({ children }) {
  const [theme, setTheme] = useState("light");

  return (
    <ThemeContext.Provider value={{ theme, setTheme, abc: 1 }}>  //上面创建的context, value是要传递的值
      {children}
    </ThemeContext.Provider>
  );
}

function Button() {
  // 使用 useContext 获取 Context 中的数据
  const { theme, setTheme } = useContext(ThemeContext);  //获取祖先元素传递的值
  return (
    <button onClick={() => setTheme(theme === "light" ? "dark" : "light")}>
      Switch to {theme === "light" ? "Dark" : "Light"} Theme
    </button>
  );
}

function App() {
  return (
    <>
      <ThemeProvider>
      <Button />
      </ThemeProvider>
    </>
  );
}

export default App;

4. useImperativeHandle

类似于vue3中的‌defineExpose(),用于暴露子组件指定的函数给父组件使用

import React, { useRef, useImperativeHandle, forwardRef } from 'react';

// 子组件
const ChildComponent = forwardRef((props, ref) => {
  const inputRef = useRef(null);

  useImperativeHandle(ref, () => ({
    focus: () => {
      if (inputRef.current) {
        inputRef.current.focus();
      }
    },
  }));

  return (
    <div>
      <input ref={inputRef} type="text" placeholder="Focus me!" />
    </div>
  );
});

// 父组件
function ParentComponent() {
  const childRef = useRef(null);

  const handleButtonClick = () => {
    if (childRef.current) {
      childRef.current.focus(); // 调用子组件的方法
    }
  };

  return (
    <div>
      <ChildComponent ref={childRef} />
      <button onClick={handleButtonClick}>Focus the input</button>
    </div>
  );
}

export default ParentComponent;
5. useRef

类似于vue3中的ref,可以用来绑定数据和dom节点,通过.current来获取绑定的值,和useState的区别是,useRef更新值不会触发更新,它会存储上一次的值,就是重新渲染也不会重置

6. useDeferredValue

useDeferredValue是一个 React Hook,可以让你延迟更新 UI 的某些部分

import React, { useState, useDeferredValue, useEffect } from 'react';

export default function SearchInput() {
  const [searchText, setSearchText] = useState('');
  const deferredSearchText = useDeferredValue(searchText);

  useEffect(() => {
    console.log('Updating search text:', deferredSearchText);
  }, [deferredSearchText]);

  return (
    <div>
      <input
        type="text"
        value={searchText}
        onChange={(e) => setSearchText(e.target.value)}
      />
      <p>Current search text: {deferredSearchText}</p>
    </div>
  );
}

看起来和防抖挺像的,但是二者是有区别的
与防抖或节流不同,useDeferredValue不需要选择任何固定延迟时间。如果用户的设备很快(比如性能强劲的笔记本电脑),延迟的重渲染几乎会立即发生并且不会被察觉。如果用户的设备较慢,那么列表会相应地“滞后”于输入,滞后的程度与设备的速度有关。

此外,与防抖或节流不同,useDeferredValue执行的延迟重新渲染默认是可中断的。这意味着,如果 React 正在重新渲染一个大型列表,但用户进行了另一次键盘输入,React 会放弃该重新渲染,先处理键盘输入,然后再次开始在后台渲染。相比之下,防抖和节流仍会产生不顺畅的体验,因为它们是阻塞的:它们仅仅是将渲染阻塞键盘输入的时刻推迟了。

如果你要优化的工作不是在渲染期间发生的,那么防抖和节流仍然非常有用。例如,它们可以让你减少网络请求的次数。你也可以同时使用这些技术。
所以useDeferredValue更适用于延迟更新页面渲染

7. useReducer

状态管理仓库,用法和vuex有一点相似

import { useReducer, useEffect, useDeferredValue } from 'react';

// 定义 action 类型
const SET_SEARCH_TEXT = 'SET_SEARCH_TEXT';

// 定义 reducer 函数
function reducer(state, action) {
  console.log(state,action,"state");
  switch (action.type) {
    case SET_SEARCH_TEXT:
      return { ...state, searchText: action.payload };
    default:
      return state;
  }
}

// 定义初始状态
const initialState = { searchText: '123' };

function SearchInput() {
  const [state, dispatch] = useReducer(reducer, initialState);
  const deferredSearchText = useDeferredValue(state.searchText);

  useEffect(() => {
    console.log('Updating search text:', deferredSearchText);
  }, [deferredSearchText]);

  return (
    <div>
      <input
        type="text"
        value={state.searchText}
        onChange={(e) => dispatch({ type: SET_SEARCH_TEXT, payload: e.target.value })}
      />
      <p>Current search text: {deferredSearchText}</p>
    </div>
  );
}

export default SearchInput;

搭配useContext函数,能做到全局状态管理

8. useTransition

useTransition是一个帮助你在不阻塞 UI 的情况下更新状态的 React Hook。
通过 transition,UI 仍将在重新渲染过程中保持响应性。例如用户点击一个选项卡并且这个选项卡的内容需要大量时间渲染,但改变了主意并点击另一个选项卡,他们可以在不等待第一个重新渲染完成的情况下完成操作

import React, { useState, useTransition } from "react";

export default function Example() {
  const [count, setCount] = useState(0);

  // 启用过渡
  const [isPending, startTransition] = useTransition();

  // 使用 startTransition 来开始一个过渡
  function handleClick() {
    startTransition(() => {
      setCount(count + 1);
    });
  }

  return (
    <div>
      <p>You clicked {count} times</p>
      <button onClick={handleClick}>
        Click me {isPending ? "(pending)" : ""}
      </button>
    </div>
  );
}

五、内置组件使用

1. Suspense

<Suspense>允许在子组件完成加载前展示后备方案

<Suspense fallback={<Loading />}>
  <SomeComponent />
</Suspense>
  • children:真正的 UI 渲染内容。如果 children 在渲染中被挂起,Suspense 边界将会渲染fallback
  • fallback:真正的 UI 未渲染完成时代替其渲染的备用 UI,它可以是任何有效的 React 节点。后备方案通常是一个轻量的占位符,例如表示加载中的图标或者骨架屏。当 children 被挂起时,Suspense 将自动切换至渲染 fallback;当数据准备好时,又会自动切换至渲染 children。如果 fallback 在渲染中被挂起,那么将自动激活最近的 Suspense 边界

六、 API使用

1. createContext

使用createContext创建组件能够提供与读取的 上下文(context)

const SomeContext = createContext(defaultValue)
  • defaultValue:当读取上下文的组件上方的树中没有匹配的上下文时,希望该上下文具有的默认值。倘若没有任何有意义的默认值,可指定其为 null。该默认值是用于作为“最后的手段”的后备方案。它是静态的,永远不会随时间改变。
  • SomeContext.Provider让你为被它包裹的组件提供上下文的值
function App() {
  const ThemeContext= createContext("默认值")
  const [theme, setTheme] = useState('light');
  // ……
  return (
    <ThemeContext.Provider value={theme}>  // value是要传递下去的值
      <Page />
    </ThemeContext.Provider>
  );
}
2.forwardRef

forwardRef允许组件使用 ref 将 DOM 节点暴露给父组件

import { forwardRef } from 'react';

const CustomInput = forwardRef((props, ref) => {
  return <input type="text" ref={ref} {...props} />;
});

// 使用 forwardRef 定义的组件
function App() {
  const inputRef = React.useRef(null);

  // 在父组件中使用 ref
  return (
    <div>
      <CustomInput ref={inputRef} />
      <button onClick={() => inputRef.current.focus()}>
        Focus Input
      </button>
    </div>
  );
}
3. lazy

lazy能够让你在组件第一次被渲染之前延迟加载组件的代码

import { lazy } from 'react';
const MarkdownPreview = lazy(() => import('./MarkdownPreview.js')); 
4. memo

memo允许你的组件在 props 没有改变的情况下跳过重新渲染

const MemoizedComponent = memo(SomeComponent, arePropsEqual?)
  • Component:要进行记忆化的组件。memo 不会修改该组件,而是返回一个新的、记忆化的组件。它接受任何有效的 React 组件,包括函数组件和forwardRef组件
  • 可选参数arePropsEqual:一个函数,接受两个参数:组件的前一个 props 和新的 props。如果旧的和新的 props 相等,即组件使用新的 props 渲染的输出和表现与旧的 props 完全相同,则它应该返回 true。否则返回false。通常情况下,你不需要指定此函数。默认情况下,React 将使用Object.is比较每个 prop
5. startTransition

startTransition可以让你在不阻塞 UI 的情况下更新 state

startTransition(scope)

startTransitionuseTransition非常相似,但它不提供isPending标志来跟踪一个 Transition 是否正在进行。你可以在useTransition不可用时调用startTransition。例如,在组件外部(如从数据库中)使用 startTransition

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值