ReactJs笔记摘录

2023-12-13 03:43:15

前言

以前2018年搞过一段时间react + antd开发,兜兜转转又回到react世界。本文力图记录一些易忘易混的知识点。

目录结构

├─package-lock.json
├─package.json ------------ // 项目配置
├─postcss.config.js ------- // postcss 配置
├─public ------------------ // 公开目录
│?├─favicon.ico
│?└─index.html
├─README.md ---------- // 项目说明
└─src --------------------- // 源码目录
├─App.tsx --------------- // 根组件
├─assets ---------------- // 静态资源目录
│?└─images -------------- // 图片目录
│?    └─logo.png ------ // logo 图片
│?└─svg -------------- // 图标目录
│?    └─logo.svg ------ // logo 图片
├─components ------------ // 公共组件目录
│?└─Menu ---------------- // 公共组件
│   ├─index.tsx ---------- // 组件文件
│   └─index.scss ------ // 组件样式
├─constants ---------------- // 项目常量
│?└─index.ts
├─libs ------------------ // 第三方库目录
├─index.tsx --------------- // 主入口
├─router ---------------- // 路由配置
│?└─index.tsx
├─store ----------------- // 状态管理
│?├─module.ts // 模块
│?└─index.ts // 入口
├─tests ----------------- // 测试目录
├─utils ----------------- // 工具目录
│?├─a.ts
│?└─index.ts
└─pages ----------------- // 视图目录
  └─Home ---------------- // 页面组件
    ├─components -------- // 子组件目录
    │?└─Header
    │   ├─index.tsx
    │   └─index.scss
    └─index.tsx ---------- // 组件主体

组件

动态组件

实现方式一:

import React, {lazy} from 'react';
const Component = lazy(() => import(`../a`));

高阶组件

React高阶组件(HOC),是灵活使用react组件的一种技巧,高阶组件本身不是组件,它是一个参数为组件,返回值也是一个组件的函数。

Hook函数

Hook 是 React 16.8 的新增特性。它可以让你在不编写 class 的情况下使用 state 以及其他的 React 特性。它们可以帮助我们简化组件的代码并提高代码的可维护性。

Hook 本质就是 JavaScript 函数,但是在使用它时需要遵循两条规则。react官方提供了一个 linter 插件来强制执行这些规则。

  • 只在最顶层使用 Hook。不要在循环,条件或嵌套函数中调用 Hook, 确保总是在你的 React 函数的最顶层调用他们。
  • 只在 React 函数组件中调用 Hook,也可以在自定义 Hook 中调用其他 Hook。但不要在普通的 JavaScript 函数中调用 Hook。

以下是常见的 Hook 函数:

useState

useState 是一个用于在函数组件中添加状态的 Hook 函数。它接受一个初始状态值,并返回一个数组,其中第一个值为当前状态值,第二个值为更新状态的函数。

import React, { useState } from 'react';

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

 return (
   <div>
     <p>You clicked {count} times</p>
     <button onClick={() => setCount(count + 1)}>
       Click me
     </button>
   </div>
 );
}

useEffect

useEffect 是一个用于在函数组件中添加副作用的 Hook 函数。它接受一个回调函数和一个依赖数组作为参数。回调函数在组件挂载、更新或卸载时执行。依赖数组中的值发生变化时,也会重新执行回调函数。


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

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

 useEffect(() => {
   document.title = `You clicked ${count} times`;
 }, [count]);

 return (
   <div>
     <p>You clicked {count} times</p>
     <button onClick={() => setCount(count + 1)}>
       Click me
     </button>
   </div>
 );
}

useContext

useContext 是一个用于在函数组件中使用 React Context 的 Hook 函数。它接受一个 Context 对象作为参数,并返回该上下文的当前值。

import React, { createContext, useContext } from 'react';

const ThemeContext = createContext('light');

function Example() {
 return (
   <ThemeContext.Provider value="dark">
     <p>Current theme: {useContext(ThemeContext)}</p>
   </ThemeContext.Provider>
 );
}

useReducer

useReducer 是一个用于在函数组件中使用状态 reducer 的 Hook 函数。它接受一个 reducer 函数和一个初始状态值作为参数,并返回一个数组,其中第一个值为当前状态值,第二个值为更新状态的函数。

import React, { useReducer } from 'react';

function App() {
 const [state, dispatch] = useReducer(reducer, initialState);

 return (
   <div>
     <p>Current state: {state}</p>
   </div>
 );
}

function reducer(state, action) {
 switch (action.type) {
   case 'increment':
     return state + 1;
   case 'decrement':
     return state - 1;
   default:
     throw new Error();
 }
}

useCallback

useCallback 是一个用于在函数组件中缓存函数的 Hook 函数。它接受一个函数作为参数,并返回一个缓存函数。如果传入的函数没有发生变化,则返回上次缓存的结果;否则,会重新创建一个新的缓存函数。

import React, { useState, useCallback } from 'react';

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

 const handleClick = useCallback(() => {
   setCount(count + 1);
 }, [count]);

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

useMemo

useMemo 是一个用于在函数组件中缓存计算结果的 Hook 函数。它接受一个函数作为参数,并返回缓存的结果。如果传入的函数没有发生变化,则返回上次缓存的结果;否则,会重新计算新的结果。

import React, { useState, useMemo } from 'react';

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

 const doubleCount = useMem

JSX语法

根元素与斜杠

注意局部的jsx片段也要加根元素:

return (
 <div>
  {items.map((item) => (
   // 此处只能有一个根元素!!!
   <>
     ...
     <div className="flex min-w-fit min-h-fit h-24 w-24 border-4 rounded-md bg-white-100 justify-center">
         // img后要加斜杠
         <img loading="lazy" src="http://www.icoolcms.com/cms/link/logo/2384"/>
     </div>
   </>
 ))}
 </div>
)

使用变量

<img className="logoImg" loading="lazy" src={`http://www.icoolcms.com/cms/link/logo/${item.linkId}`}/>

推荐使用className替代class

className属性是React中使用的属性名称,用于避免与JavaScript的保留关键字class冲突。

属性写法

属性名称: React DOM 使用小驼峰命令来定义属性的名称,而不使用 HTML 属性名称的命名约定;
style 样式属性: 采用小驼峰命名属性的 JavaScript 对象。例如:

<img loading="lazy" src={`http://www.icoolcms.com/cms/link/logo/${item.linkId}`} 
                      style={{width:'44px', height:'44px', borderRadius:'8px'}}/>      

注意样式属性:backgroundColor和borderRadius,不能写成css里的background-color和border-radius

三元表达式 vs &&

这两种写法很常用:

<div>
      Hello {name}
      {isPro ? '?' : null}
       {stars && (
      <div>
        Stars:{'☆'.repeat(stars)}
      </div>
    )}
</div>

antd和tailwindcss

可以在Antd组件中直接使用Tailwindcss类:

<Button className="bg-blue-500 hover:bg-blue-700 text-white font-bold py-2 px-4 rounded">
  Click me
</Button>

组件通信

React 最推荐的数据交互方式是:props & onChnage。在这种交互方式里:对于一个可视组件 ComponentA,用 props 来向它发送信息,而用 onChange 回调函数来接收 ComponentA 发送的信息。在程序世界里,我们更喜欢把上述「交互方式」称为「接口」,虽然这个「接口」不是我们在面向对象语言里的 interface,但是跟 interface 有着类似的功能。

父传子:props和自定义函数事件

// 父组件
function Money() {
    const [selected, setSelected] = useState('food');
    return (
            <TagsSection value={selected}/>
    );
}

// 子组件
type Props = {
  //声明props的数据类型
    value: string;
}

const TagsSection: React.FC<Props> = (props) => {
    const tagName = props.value;
    return (
        <div>the tag name is { tagName }</div>
    );
};

子传父:props.onchange

import React, { useState } from "react";

const MyComponent = ({ onChange, initialValue }) => {
 const [value, setValue] = useState(initialValue);

 const handleChange = (event) => {
   setValue(event.target.value);
   onChange(event.target.value);
 };

 return (
   <input
     type="text"
     value={value}
     onChange={handleChange}
   />
 );
};

const App = () => {
 const handleValueChange = (value) => {
   console.log(`Value changed to: ${value}`);
 };

 return (
   <MyComponent onChange={handleValueChange} initialValue="Initial value" />
 );
};

export default App;

在这个示例中,我们定义了一个名为MyComponent的React组件,它接受一个onChange属性和一个initialValue属性。onChange属性定义了当组件的值发生变化时应该调用的函数,而initialValue属性则定义了组件的初始值。

在MyComponent组件内部,我们使用useState Hook来存储组件的值。然后,我们定义了一个handleChange函数,该函数在用户输入发生变化时被调用。在这个函数中,我们调用setValue来更新组件的值,并调用onChange函数来通知父组件值已更改。

最后,我们定义了一个App组件,该组件使用MyComponent组件,并将handleValueChange函数作为onChange属性的值传递。当组件的值发生变化时,handleValueChange函数将被调用,并输出新的值。

Context

Context 的使用方式:

  • 使用 const xxx = createContext(null) 创建上下文
  • 使用 <xxx.Provider> 圈定作用域
  • 在作用域内使用 useContext(xxx) 来使用上下文

先创建一个 Context 对象:

import React from "react";

const ThemeContext = React.createContext();

export default ThemeContext;

再创建Provider:

import React from "react";
import ThemeContext from "./ThemeContext";

const ThemeProvider = ({ theme }) => {
 return (
   <ThemeContext.Provider value={theme}>
     {/* 这里可以传递其他内容 */}
   </ThemeContext.Provider>
 );
};

export default ThemeProvider;

再创建Consumer, 它将使用 ThemeContext 上下文来获取 theme 数据

import React from "react";
import ThemeContext from "./ThemeContext";

const ThemeConsumer = ({ children }) => {
 return (
   <ThemeContext.Consumer>
     {theme => {
       // 在这里可以使用 theme 数据
       return <>{children}</>;
     }}
   </ThemeContext.Consumer>
 );
};

export default ThemeConsumer;

连起来:

import React from "react";
import ThemeProvider from "./ThemeProvider";
import ThemeConsumer from "./ThemeConsumer";

const theme = {
 color: "blue",
};

const App = () => {
 return (
   <ThemeProvider theme={theme}>
     <ThemeConsumer>
       {({ theme }) => (
         <div>
           <h1>Theme color: {theme.color}</h1>
         </div>
       )}
     </ThemeConsumer>
   </ThemeProvider>
 );
};

export default App;

在这个示例中,我们首先创建了一个 ThemeContext 对象,然后创建了一个 ThemeProvider 组件,它将提供 theme 数据。接下来,我们创建了一个 ThemeConsumer 组件,它将使用 ThemeContext 上下文来获取 theme 数据。最后,我们创建了一个 Root 组件,它将使用 ThemeProvider 组件提供 theme 数据,并使用 ThemeConsumer 组件来获取 theme 数据。

如果不用Consumer,也可以直接使用useContext()获得context数据:

// 使用useContext Hook
const { isLoggedIn, login, logout } = useContext(AuthContext);

状态管理

业内常规的成熟方案一般有:mobx、redux等专门的全局状态管理库,相对而言基本可以覆盖支持所有的复杂业务场景,再也就简单粗暴通过多个Context嵌套来实现共享。Redux 是借鉴 Flux 开发的,它们都是单向数据流,而 MobX 则有所不同,它是基于观察者模式实现。

React18以后的版本内置了useReducer() hook,它是 useState() 的替代品,简单的状态可以直接使用 useState,当我们遇到复杂多层级的状态或者下个状态要依赖上个状态时使用 useReducer() 则非常方便,在配合 Context 与 useContext() 就能实现类似 Redux 库的功能。

  • 当我们项目中复杂程度较低时,建议只用state就可以了
  • 如果仅仅因为存在多层传递数据的场景,不建议使用mobx或redux,可使用context解决
  • 如果仅仅因为夸路由数据共享,不建议使用mobx或redux,可使用context或者路由传参解决
  • 如果业务复杂,需要使用第三方状态管理解决复杂度,看下一条
  • 当项目复杂度一般,小规模团队或开发周期较短、要求快速上线时,推荐使用mobx
  • 当项目复杂度较高,团队规模较大或要求对事件分发处理可监控可回溯时,推荐使用redux,可尝试使用 rematch或@reduxjs/toolkit,减少模板代码

结合useContext()和useReducer()实现全局状态共享

写一个store.tsx:

import React, { createContext, useReducer, useContext } from 'react';

const initialState = { count: 0 };

function reducer(state, action) {
  switch (action.type) {
    case 'increment':
      return { count: state.count + 1 };
    case 'decrement':
      return { count: state.count - 1 };
    default:
      throw new Error();
  }
}


const StateContext = createContext();
const DispatchContext = createContext();

function useStateStore() {
  return useContext(StateContext);
}

function useDispatchStore() {
  return useContext(DispatchContext);
}

function StoreProvider({ children }) {
  const [state, dispatch] = useReducer(reducer, initialState);

  return (
    <StateContext.Provider value={state}>
      <DispatchContext.Provider value={dispatch}>
        {children}
      </DispatchContext.Provider>
    </StateContext.Provider>
  );
}

export { useStateStore, useDispatchStore, StoreProvider };

修改状态:

    import React from 'react';
    import { useDispatchStore } from './store';

    function Producer() {
      const dispatch = useDispatchStore();
      console.log('header udpate');

      return (
        <>
          <button onClick={() => dispatch({type: 'decrement'})}>-</button>
          <button onClick={() => dispatch({type: 'increment'})}>+</button>
        </>
      );
    }

    export default Producer;

消费状态:

    import React from 'react';
    import { useStateStore } from './store';

    function Footer() {
      const state = useStateStore();
      console.log('footer udpate');

      return (
        <p>{state.count}</p>
      );
    }

    export default Footer;

UI 问题

选择哪个UI框架?

除了大名鼎鼎的antd之外,其实还有很多选择。react-bootstrap也是堂堂正正,大部分UI组件都不错。daisyui也冉冉升起。

编程实践

  • 命名:props、变量用小驼峰,组件的文件名用大驼峰
  • 引号:JSX 属性中用双引号("),但是在js里用单引号(')
  • 自闭合标签:在自闭和标签内空一格,如<Foo />,当没有子元素时,最好用自闭合标签
  • 推荐用 ref callback 函数
    useRef() 返回一个具有单个 current 属性 的 ref 对象,并初始化为你提供的初始值
// bad
<Foo
  ref="myRef"
/>

// good
<Foo
  ref={(ref) => { this.myRef = ref; }}
/>
  • 用箭头函数关闭局部变量
  • 构造函数里绑定事件处理函数
  • 不要在 React 组件里使用下划线作为内部方法名前缀
  • State 的更新可能是异步的
  • Refs 提供了一种方式,允许我们访问 DOM 节点或在 render 方法中创建的 React 元素 。我们推荐使用 createRef API 的方式 或者 回调函数的方式使用 Refs ,而不是使用 this.refs.textInput 这种过时的方式访问 refs ,因为它存在一些 问题。
  • 建议使用路由懒加载当前用户所需要的内容

const OtherComponent = React.lazy(() => import(‘./OtherComponent’));

参考链接

React基础文档

React使用

UI框架

组件

文章来源:https://blog.csdn.net/jgku/article/details/130253917
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。