react--

71 阅读3分钟

1. 组件基础

// 函数组件
const FunctionComponent = (props) => {
  // props 是只读的,不能修改
  const { name, age } = props
  
  // 返回 JSX
  return (
    <div>
      <h1>Hello, {name}</h1>
      <p>Age: {age}</p>
    </div>
  )
}

// 类组件
class ClassComponent extends React.Component {
  // 构造函数,初始化 state
  constructor(props) {
    super(props) // 必须调用 super(props)
    this.state = {
      count: 0
    }
    // 方法绑定 this
    this.handleClick = this.handleClick.bind(this)
  }
  
  // 生命周期方法
  componentDidMount() {
    // 组件挂载后执行,适合做初始化工作
    console.log('Component mounted')
  }
  
  componentDidUpdate(prevProps, prevState) {
    // 组件更新后执行,可以比较前后 props 和 state
    if (prevState.count !== this.state.count) {
      console.log('Count changed')
    }
  }
  
  componentWillUnmount() {
    // 组件卸载前执行,适合做清理工作
    console.log('Component will unmount')
  }
  
  // 事件处理方法
  handleClick() {
    // 使用 setState 更新状态
    this.setState((prevState) => ({
      count: prevState.count + 1
    }))
  }
  
  render() {
    return (
      <div>
        <p>Count: {this.state.count}</p>
        <button onClick={this.handleClick}>Increment</button>
      </div>
    )
  }
}

2. Hooks 使用

import React, { useState, useEffect, useCallback, useMemo, useRef } from 'react'

const HooksExample = () => {
  // useState: 状态管理
  const [count, setCount] = useState(0) // 初始值为 0
  const [user, setUser] = useState(null) // 初始值为 null
  
  // useRef: 引用 DOM 元素或保存任意可变值
  const inputRef = useRef(null) // 用于引用 input 元素
  const timerRef = useRef(null) // 用于保存定时器 ID
  
  // useEffect: 副作用处理
  useEffect(() => {
    // 组件挂载时执行
    console.log('Component mounted')
    
    // 启动定时器
    timerRef.current = setInterval(() => {
      setCount(c => c + 1)
    }, 1000)
    
    // 返回清理函数,组件卸载时执行
    return () => {
      clearInterval(timerRef.current)
    }
  }, []) // 空依赖数组表示只在挂载和卸载时执行
  
  // useEffect: 数据获取
  useEffect(() => {
    const fetchUser = async () => {
      try {
        const response = await fetch('https://api.example.com/user')
        const data = await response.json()
        setUser(data)
      } catch (error) {
        console.error('Failed to fetch user:', error)
      }
    }
    
    fetchUser()
  }, []) // 空依赖数组表示只在挂载时获取数据
  
  // useCallback: 记忆化回调函数
  const handleClick = useCallback(() => {
    setCount(c => c + 1)
  }, []) // 空依赖数组表示函数永远不变
  
  // useMemo: 记忆化计算结果
  const expensiveValue = useMemo(() => {
    // 复杂计算
    return count * 2
  }, [count]) // 只在 count 变化时重新计算
  
  return (
    <div>
      <p>Count: {count}</p>
      <p>Expensive Value: {expensiveValue}</p>
      <button onClick={handleClick}>Increment</button>
      
      <input
        ref={inputRef} // 引用 DOM 元素
        type="text"
        placeholder="Type something..."
      />
      
      {user && (
        <div>
          <h2>User Info</h2>
          <p>Name: {user.name}</p>
          <p>Email: {user.email}</p>
        </div>
      )}
    </div>
  )
}

3. 自定义 Hook

// 自定义 Hook:管理表单状态
function useForm(initialValues = {}) {
  // 表单状态
  const [values, setValues] = useState(initialValues)
  // 错误状态
  const [errors, setErrors] = useState({})
  
  // 处理输入变化
  const handleChange = useCallback((e) => {
    const { name, value } = e.target
    setValues(prev => ({
      ...prev,
      [name]: value
    }))
  }, [])
  
  // 表单验证
  const validate = useCallback(() => {
    const newErrors = {}
    // 验证规则
    Object.entries(values).forEach(([key, value]) => {
      if (!value) {
        newErrors[key] = 'This field is required'
      }
    })
    setErrors(newErrors)
    return Object.keys(newErrors).length === 0
  }, [values])
  
  // 表单提交
  const handleSubmit = useCallback((onSubmit) => {
    return (e) => {
      e.preventDefault()
      if (validate()) {
        onSubmit(values)
      }
    }
  }, [values, validate])
  
  // 重置表单
  const reset = useCallback(() => {
    setValues(initialValues)
    setErrors({})
  }, [initialValues])
  
  return {
    values,
    errors,
    handleChange,
    handleSubmit,
    reset
  }
}

// 使用自定义 Hook
const LoginForm = () => {
  const { values, errors, handleChange, handleSubmit } = useForm({
    email: '',
    password: ''
  })
  
  const onSubmit = async (data) => {
    try {
      // 发送登录请求
      const response = await api.login(data)
      console.log('Login success:', response)
    } catch (error) {
      console.error('Login failed:', error)
    }
  }
  
  return (
    <form onSubmit={handleSubmit(onSubmit)}>
      <div>
        <input
          name="email"
          value={values.email}
          onChange={handleChange}
          placeholder="Email"
        />
        {errors.email && <span>{errors.email}</span>}
      </div>
      
      <div>
        <input
          type="password"
          name="password"
          value={values.password}
          onChange={handleChange}
          placeholder="Password"
        />
        {errors.password && <span>{errors.password}</span>}
      </div>
      
      <button type="submit">Login</button>
    </form>
  )
}

4. Context API

// 创建 Context
const ThemeContext = React.createContext({
  theme: 'light',
  toggleTheme: () => {}
})

// Context Provider
const ThemeProvider = ({ children }) => {
  // 主题状态
  const [theme, setTheme] = useState('light')
  
  // 切换主题方法
  const toggleTheme = useCallback(() => {
    setTheme(t => t === 'light' ? 'dark' : 'light')
  }, [])
  
  // 提供 context 值
  const value = {
    theme,
    toggleTheme
  }
  
  return (
    <ThemeContext.Provider value={value}>
      {children}
    </ThemeContext.Provider>
  )
}

// 自定义 Hook 使用 Context
const useTheme = () => {
  const context = useContext(ThemeContext)
  if (!context) {
    throw new Error('useTheme must be used within ThemeProvider')
  }
  return context
}

// 使用 Context 的组件
const ThemedButton = () => {
  // 使用自定义 Hook 获取主题
  const { theme, toggleTheme } = useTheme()
  
  return (
    <button
      style={{
        backgroundColor: theme === 'light' ? '#fff' : '#000',
        color: theme === 'light' ? '#000' : '#fff'
      }}
      onClick={toggleTheme}
    >
      Toggle Theme
    </button>
  )
}

// 应用入口
const App = () => {
  return (
    <ThemeProvider>
      <div>
        <h1>Themed App</h1>
        <ThemedButton />
      </div>
    </ThemeProvider>
  )
}

5. 性能优化

import React, { memo, useCallback, useMemo } from 'react'

// 使用 memo 包装组件,只在 props 变化时重新渲染
const ExpensiveComponent = memo(({ data, onItemClick }) => {
  console.log('ExpensiveComponent render')
  
  return (
    <ul>
      {data.map(item => (
        <li key={item.id} onClick={() => onItemClick(item)}>
          {item.name}
        </li>
      ))}
    </ul>
  )
})

const ParentComponent = () => {
  const [items, setItems] = useState([])
  const [count, setCount] = useState(0)
  
  // 使用 useCallback 记忆化回调函数
  const handleItemClick = useCallback((item) => {
    console.log('Item clicked:', item)
  }, []) // 空依赖数组表示函数永远不变
  
  // 使用 useMemo 记忆化计算结果
  const processedData = useMemo(() => {
    return items.map(item => ({
      ...item,
      processed: true
    }))
  }, [items]) // 只在 items 变化时重新计算
  
  return (
    <div>
      <button onClick={() => setCount(c => c + 1)}>
        Count: {count}
      </button>
      
      <ExpensiveComponent
        data={processedData}
        onItemClick={handleItemClick}
      />
    </div>
  )
}
  • 组件基础(函数组件和类组件)

  • Hooks 的使用(useState, useEffect, useCallback, useMemo, useRef)

  • 自定义 Hook

  • Context API

  • 性能优化(memo, useCallback, useMemo)