工程笔记

文档

本篇目录

文档

学习源码推荐看一遍 Build Your Own React,了解基本流程,再边调试源码边看源码流程图

手写 Context 主题切换

Theme-context.tsx 通过 createContext 创建一个 Context 对象,传入默认值,同事封装一个 hook 用于获取 Context 对象

import { createContext, useContext } from 'react'

export type Theme = 'light' | 'dark'

type ThemeContextType = {
  theme: Theme
  setTheme: (theme: Theme) => void
}

export const ThemeContext = createContext<ThemeContextType>({
  theme: 'dark',
  setTheme: () => {}
})

export const useTheme = () => {
  const context = useContext(ThemeContext)
  if (context === undefined) {
    throw new Error('useTheme must be used within a ThemeProvider')
  }
  return context
}

Theme-provider.tsx,通过 value 属性提供 Context 对象

import { useState } from 'react'
import { ThemeContext, type Theme } from './theme-context'

export const ThemeProvider = ({ children }: { children: React.ReactElement }) => {
  const [theme, setTheme] = useState<Theme>('dark')

  const value = {
    theme,
    setTheme
  }

  return <ThemeContext.Provider value={value}>{children}</ThemeContext.Provider>
}

Consumer.tsx,Consumer 组件,用于消费 Context 对象, useContext 在前面已经封装好了

import { ThemeContext } from './theme-context'
import { ThemeProvider } from './theme-provider'
import { useContext } from 'react'

const InnerContext = () => {
  const { theme, setTheme } = useContext(ThemeContext)

  return (
    <div className="size-40 bg-gray-200">
      <button className="cursor-pointer" onClick={() => setTheme('light')}>
        切换主题
      </button>
      <p>当前主题: {theme}</p>
    </div>
  )
}

const ContextTheme = () => {
  return (
    <ThemeProvider>
      <InnerContext />
    </ThemeProvider>
  )
}

export default ContextTheme

React 优化将变的部分与不变部分分离

普通的性能优化有 React.memo 和 useMemo 和 useCallback,React.lazy 等

将变的部分与不变部分分离,这种思路可以减少父组件更新,子组件也更新,减少不必要的渲染

父组件(父组件更新,子组件也更新)

import { useState } from 'react'
import Expensive from './Expensive'

const Index = () => {
  const [count, setCount] = useState(0)

  return (
    <div>
      <div>count: {count}</div>
      <br />
      <button className="cursor-pointer rounded-md bg-blue-500 p-2 text-white" onClick={() => setCount(count + 1)}>
        点击加1
      </button>
      <br />
      <br />
      <Expensive />
    </div>
  )
}

export default Index

子组件(耗时组件)

const Expensive = () => {
  console.log('Expensive 组件渲染')

  const now = performance.now()

  while (performance.now() - now < 1000) {
    // 模拟耗时操作
  }

  return (
    <div>
      <div>Expensive</div>
    </div>
  )
}

export default Expensive

可以把父组件需要更新的部分,也就是下面这段代码单独抽离成一个组件

<div>count: {count}</div>
<br />
<button
  className="bg-blue-500 text-white p-2 rounded-md cursor-pointer"
  onClick={() => setCount(count + 1)}
>
  点击加1
</button>

手写 KeepAlive