跳至主要內容

base

chanchaw大约 9 分钟react

jsx

下面代码演示在 jsx 中使用字符串、变量、调用函数、调用方法以及对象的使用。注意下面的花括号只支持表达式,并不支持语句,例如:if,switch,变量声明等等


function getName(){
  return 'china';
}

// 列表循环的使用
function demoFor(){
  const list = [
    {id: 1001, name: 'React'},
    {id: 1002, name: 'Angular'},
    {id: 1003, name: 'Vue'},
  ]
  // DOM节点也被视作变量,同理在 JSX 中要使用花括号表现出来
  const h1 = <h1>这里是DOM节点变量</h1>
  return (
    <ul>
      {list.map(item => <li key={item.id}>{item.name}</li>)}
    </ul>
    {h1}
  )
}

// 条件表达式
function conditionalExpress(){
  const b = false
  // 样式对象: 样式作为对象变量可直接赋值给DOM节点
  const style = { color: 'red', fontSize: 22 }
  const name = <span style={style}>演示样式对象变量</span>
    
  return (
    <div className = 'App'>
      {/* 逻辑与 */}
      {b && <div>应该显示了吧</div>}
      {b ? <span>true时显示</span> : <span>false时显示</span>}
    </div>
  )
}

// 绑定事件
function handleBtn(){
  alert('点击了按钮');
}

// 演示点击事件
function buttonDemo(){
  return (
    <div className = 'App'>
      <button onClick={handleBtn}>点我显示alert</button>
    </div>
  )
}

// 打印事件对象e
function buttonDemo(){
  const handleBtn = e => {
    console.log(e)
  }

  return (
    <div className = 'App'>
      <button onClick={handleBtn}>点我显示alert</button>
    </div>
  )
}

// 绑定事件带参数
function btnParam(){
  const eve = name => {
    console.log(`input name is : ${name}`);
  }

  return (
    <div className = 'App'>
    <button onClick={ () => eve('chanchaw') }>点我显示alert</button>
  </div>
  )
}

// 绑定事件带参数+事件对象
function btnParam01(){
  const eve = (e, name) => {
    console.log(`input name is : ${name}`, e);
  }

  return (
    <div className = 'App'>
      <button onClick={ e => eve(e,'chanchaw') }>点我显示alert</button>
    </div>
  )
}

// useState改变对象属性的用法
function useStateDemoObj(){
  const [form, setForm] = useState({
    id: 1001, name: '小张'
  });

  const handleClick = () => {
    setForm({
      ...form,
      name: '小高'
    })
  }

  return (
    <div className = 'App'>
      <p>id:{form.id}</p>
      <p>name:{form.name}</p>
      <button onClick={handleClick}>点击改变</button>
    </div>
  )
}
       
function App(){
  const aa = 101;
  const fontColor = 'blue';

  return (
    <div className = "App">
      <p>this is React App</p>

      {/* 显示硬编码的字符串 */}
      <p>{'硬编码的字符串'}</p>

      {/* 显示 JS 变量 */}
      <p>{aa}</p>

      {/* 函数调用 */}
      {getName()}

      {/* 方法调用 */}
      <p>
        今天的日期
      {new Date().getDate()}
      </p>

      {/* 使用 js 对象 */}
      <div style={{ color: fontColor }}>this is a div</div>
    </div>
  )
}

export default App

模块与导出

第一种方法制作的模块在使用时要具名导出

function add(){...}
function sub(){...}
export { add, sub}

# 上面的方式制作的模块,在业务组件中使用方法:
import { add, sub } from './ccUtils'

第二种方式更像调用 java 类的方法

export default {
  set(key: string, val: any): void {
    localStorage.setItem(key, JSON.stringify(val))
  },
  get(key: string): any {
    const val = localStorage.getItem(key)
    if (!val) return ''
    try {
      return JSON.parse(val)
    } catch {
      return val
    }
  },
  remove(key: string): void {
    localStorage.removeItem(key)
  },
  clear(): void {
    localStorage.clear()
  }
}

            
# 业务组件中使用方法如下:
import localStorage from '../utils/localStorage'
localStorage.set();
localStorage.get();

组件

封装

下面的 props.children 表示父组件中使用本封装组件时使用双标签样式中间插入的组件,效果类似 vue 中的 slot

export default function SearchForm(props: any){
    return(
    	<Form className='search-form' form={props.form} layout='inline' initialValues={props.initialValues}>
            {props.children}
            <Form.Item>
                <Space>
                    <Button>搜索</Button>
                    <Button>重置</Button>
                </Space>
            </Form.Item>
        </Form>
    )
}

组件分类mermaid

graph LR
A(组件) --> B1(类组件)
A --> B2(函数式组件)
A --> B3(公共规范)
B1 --> C11(继承Component)
B1 --> C12(必须使用render函数并返回jsx)
B1 --> C13(constructor是可选.必要的初始化)
B3 --> C31(首字母大写)
click B2 "#函数式组件最简案例"
click B1 "#类组件最简案例"

组件分类markmap

---
markmap:
  colorFreezeLevel: 2
---

# markmap

## 链接

- <https://markmap.js.org/>
- [GitHub](https://github.com/markmap/markmap)

## 功能

- 链接
- **强调** ~~删除线~~ *斜体* ==高亮==
- 多行
  文字
- `行内代码`

类组件最简案例

import React from 'react'
export default class App extends React.Component {
    render() {
        return (
            <div>this is app</div>
        )
    }
}

函数式组件最简案例

react组件
react组件

样式

组件中的样式
组件中的样式
// 下面是 index.css 的全部代码
.red-btn {
    color: red
}

// 先导入样式文件
import './index.css'

// 组件内的样式
function styleDemo(){
  return (
    <div className = 'App'>
      <span className={'red-btn'}>cccc</span>
    </div>
  )
}

// 上面是导入外部样式
// 行内样式的使用,多单词时使用小驼峰的样式
function inlineStyle(){
  const sytle01 = {
    color: 'blue',
    fontSize: '50px'
  }

  return (
    <span style={sytle01}>演示行内样式</span>
  )
}

受控表单绑定

概述

DOM

获取DOM

先导入

import { useRef } from 'react'

设置焦点

import { useRef } from 'react'
const inputRef = useRef(null);
inputRef.current.focus();

<input ref={inputRef}/>

组件通信

父传子

可见,父组件传递来的参数,在子组件中以对象接收

// 组件通信 - 父传子
function ChildComp(props){
  return (
    <div>我是子组件:{props.name}</div>
  )
}

function App(){
  const fatherText = '张三';
  return (
    <div className = 'App'>
        <ChildComp name = {fatherText}/>
    </div>
  )
}

props 的特性看下图

组件通信父传子props介绍
组件通信父传子props介绍

父组件中引用子组件时,写在子组件内部的标签都被子组件的 props.children 属性接收

子传父

兄弟通信

子组件A将数据传递给父组件,再由父组件传递给子组件B

跨层组件通信

代码实现

import { createContext, useContext } from 'react'
const MsgContext = createContext()
// 第一层组件
function Level1Cmpt(){
  return (
    <div className = 'level1Cmpt'>
      <h1>我是第一层组件</h1>
      <Level2Cmpt />
    </div>
  )
}
// 第二层组件
function Level2Cmpt(){
  const msg = useContext(MsgContext);
  return (
    <div className = 'lelvel2Cmpt'>
      <span>[我是第二层组件了]</span>
      {msg}
    </div>
  )
}

// 根组件
function App(){
  const fatherText = '张三';
  const rootCmptText = '我是根组件的内容,用于测试跨组件通信';

  // 跨层级组件通信
  return (
    <div>
      <MsgContext.Provider value={rootCmptText}>
        <h1>我是根组件的标题</h1>
        <Level1Cmpt />
      </MsgContext.Provider>
    </div>
  )
}

React Hook

规则

  • 只能在组件中或者其他自定义 hook 函数中调用(自定义hook必须以 use 开头)
  • 只能在组件的顶层调用,不能嵌套在 if,for 中

useTransition

useRef

useReducer

类似 redux 的用法

useContext

用于跨层级传输数据

useState

概述

useState
useState
function useStateDemo(){
  const [count, setCount] = useState(0);
  const handleClick = () => {
    setCount(count+1);
  }
  
  return (
    <div className = 'App'>
      <p>数字:{count}</p>
      <button onClick={handleClick}>点我显示alert</button>
    </div>
  )
}

状态不可变

状态不可变
状态不可变

对象和数组

  // 更新对象时先解构再修改
  const [obj, setObj] = useState({});
  const handleObj = () => {
      setObj({...obj,name:'cc'})
  }
  
  // 同理,数组也要先解构再新增
  const [arr, setArr] = useState([]);
  const handleArr = () => {
      setArr({...arr,'cc'})
  }

渲染一次

下面3行代码累加数据,前端页面只会渲染一次,即在执行完函数 handleState 后才渲染一次视图,而不是每执行一行就渲染一次视图。

const [state, setState] = useState(0)
const handleState = ()=> {
    setState(state+1)
    setState(state+1)
    setState(state+1)
}

强制渲染(手动渲染)

通过函数 flushSync 可强制渲染视图

const [state, setState] = useState(0)
const handleState = ()=> {
    setState(state+1)
    flushSync(() => {
      setState(state+1)        
    })
    setState(state+1)
}

useEffect

概述

组件渲染完毕后会自动执行 useEffect(生命周期函数)

组件初始渲染后执行

import { useEffect } from 'react'

function httpReq(){
  const URL = 'http://17.71.0.29:8080/PADEMIS/sw/checkPallet4In'
  // 实例化一个Request实例
  // 第一个参数一般指资源路径
  // 第二个参数可以理解为请求的配置项,包含头部信息和http请求一些关键配置(请求类型、参数...)
  let requestInstance = new Request(URL, {
    method: 'post',
    headers: {
        'Content-Type': 'application/json;charset=utf-8'
    },
    body: '{"palletCode":"A0012"}'
  })
  
  // fetch方法参数同Request实例
  // 第一个参数为url或者Request实例
  // 第二个参数为请求配置项
  fetch(requestInstance).then(response => {
    // 返回的是一个Response的实例
    // 调用Response实例的序列化方法,序列化成json,返回值是一个promise
    // 序列化方法有 json,text,formData,blob,arrayBuffer,redirct
    let result = response.json()
    result.then(res => {
        console.log(res)
    })
  }).catch(err => console.log(err)).finally(() => console.log('网络请求执行完毕!'))
}

function useEffectCmpt(){
  useEffect(() => {
    httpReq()
  },[])// 空数组(没有依赖)表示只会在组件渲染之后执行一次

  return (
    <div className = 'useEffectCmpt'>
        this is app
    </div>
  )
}

依赖项参数

下面的 “组件更新” = 由组件状态数据更新导致组件重新渲染,即组件每次重新渲染都会执行一次

第三种情况 “特性依赖项变化” 一般为传入的状态变量,下面代码表示该子组件初始化完毕后执行一次,并且每次 state 变更都会执行一次

const [state, setState] = useState(0);
useEffect(() => {
    console.log('xxx');
},[state])

组件卸载后执行

//#region 组件卸载的生命周期钩子
function UnLoadHook(){
  useEffect(() => {
    // 组件初始渲染后执行
    const timer01 = setInterval(() => {
      console.log('定时器执行中...');
    }, 1000)

    // 组件被卸载后执行的逻辑
    return () => {
      clearInterval(timer01)
    }
  },[])// 空数组表示只在组件初始渲染后执行一次,之后组件更新时都不会执行

  return (
    <span>这里是测试卸载组件时清除定时器的子组件</span>
  )
}
//#endregion


function App(){
  // 控制子组件显示的状态变量
  const [visible, setVisible] = useState(true);
  const handleVisible = () => {
    setVisible(!visible)
  }

  return (
    <div>
      { visible && <UnLoadHook />}
      <button onClick={handleVisible}>显示隐藏</button>
    </div>
  )
}

useMemo

简单案例

组件内经常变动的数据使用该 hook 可以缓存数据,在依赖条件(数据)变动后才会更新数据。例如下面代码,billList 是父组件传递来的数据,当该数据变动后必然引起本子组件数据变化进而引起视图变化,此时使用 useMemo 在计算本组件使用到的数据同时缓存起来,当组件重新渲染时如果依赖项 billList 没有变化则不会调用下面逻辑重新计算得到 monthGroup,下面代码同时使用了 lodash 的分组函数,按照日期将数据分组。

const monthGroup = useMemo(() => {
    if(!billList) return []
    return _.groupBy(billList, item => formatBillDateMonthly(item.date))
},[billList])

lodash 分组后的数据类似

{'2024-07-01':[...],'2024-08-09':[...]}

联动缓存

当用户操作引起组件内状态 currentMonthList 变更时要求自动计算并缓存数据时使用 useMemo,代码如下

const [currentMonthList, setMonthList] = useState([])
const monthStatistics = useMemo(() => {
    const ret = {pay: 0, income: 0, balance: 0}
    if(!currentMonthList || currentMonthList.length === 0) return ret
    currentMonthList.forEach(item => {
        if(item.type === 'pay') ret.pay += item.money
        if(item.type === 'income') ret.income += item.money
        ret.balance += item.money
    })
    return ret
},[currentMonthList])

memo

缓存组件,类似 useMemo 用于缓存数据。

useCallback

useMemo 用来缓存数据,useCallback 用来缓存函数。即组件渲染时该函数不会被重新创建

自定义hook

自定义 hook 必须以 use 开头

//#region 自定义hook
function useHook(){
  const [visible, setVisible] = useState(true);
  const changeVisible = () => setVisible(!visible)
  return { visible, changeVisible }
}
//#endregion

function App(){
  // 自定义hook
  const { visible, changeVisible } = useHook();
  return (
    <div>
      { visible && <UnLoadHook />}
      <button onClick={ changeVisible }>显示隐藏</button>
    </div>
  )
}