【React】入门Day05 —— 知识点总结:useReducer、性能优化(涉及useMemo、React.memo、useCallback)、组件通信及zustand状态管理

一、useReducer

import { useReducer } from 'react';

// 定义reducer函数
function reducer(state, action) {
  switch (action.type) {
    case 'ADD_TODO':
      return [...state, { id: Date.now(), text: action.payload, completed: false }];
    case 'TOGGLE_TODO':
      return state.map(todo =>
        todo.id === action.payload? {...todo, completed:!todo.completed } : todo
      );
    default:
      return state;
  }
}

function TodoApp() {
  const [todos, dispatch] = useReducer(reducer, []);

  const addTodo = (text) => {
    dispatch({ type: 'ADD_TODO', payload: text });
  };

  const toggleTodo = (id) => {
    dispatch({ type: 'TOGGLE_TODO', payload: id });
  };

  return (
    <div>
      <input type="text" placeholder="Add a new todo" onKeyPress={(e) => {
        if (e.key === 'Enter') {
          addTodo(e.target.value);
          e.target.value = '';
        }
      }} />
      <ul>
        {todos.map(todo => (
          <li key={todo.id} style={{ textDecoration: todo.completed? 'line-through' : 'none' }} onClick={() => toggleTodo(todo.id)}>{todo.text}</li>
        ))}
      </ul>
    </div>
  );
}

1. 基础使用

  • 作用:让 React 管理多个相对关联的状态数据。
  • 使用步骤
    • 定义reducer函数,根据不同的action返回不同的新状态。
    • 在组件中使用useReducer分派action,得到当前状态state和用于触发reducerdispatch函数。
    • 通过调用dispatch函数传入action对象,触发reducer函数,分派action操作,更新视图。

2. 更新流程

  • 组件调用dispatch函数发送action
  • reducer函数接收当前stateaction,根据action类型计算新的state
  • 组件使用新的state更新视图。

3. 分派 action 传参

  • 分派action时如果想传递参数,需在action对象中添加payload参数放置状态参数。reducer函数根据action类型和payload值计算新状态。

二、渲染性能优化

1. useMemo

import { useMemo, useState } from 'react';

function expensiveCalculation(num) {
  console.log('执行昂贵计算');
  let result = 0;
  for (let i = 0; i < 1000000; i++) {
    result += Math.random() * num;
  }
  return result;
}

function App() {
  const [count, setCount] = useState(1);
  const [otherValue, setOtherValue] = useState('');

  const memoizedResult = useMemo(() => expensiveCalculation(count), [count]);

  return (
    <div>
      <input type="number" value={count} onChange={(e) => setCount(Number(e.target.value))} />
      <input type="text" value={otherValue} onChange={(e) => setOtherValue(e.target.value)} />
      <p>计算结果: {memoizedResult}</p>
    </div>
  );
}
  • 作用:在每次重新渲染时缓存计算结果。
  • 使用场景及优化方式
    • 当一个计算结果依赖于某个状态,但该状态变化不应该每次都重新计算时使用。
    • 将计算逻辑放在useMemo的回调函数中,并将依赖的状态作为useMemo的依赖项数组传入,只有依赖项变化时才重新计算。

2. React.memo

import React, { useState } from 'react';

const ChildComponent = React.memo(function Child({ value }) {
  console.log('子组件渲染');
  return <div>{value}</div>;
});

function App() {
  const [count, setCount] = useState(0);
  const [otherValue, setOtherValue] = useState('');

  return (
    <div>
      <input type="number" value={count} onChange={(e) => setCount(Number(e.target.value))} />
      <input type="text" value={otherValue} onChange={(e) => setOtherValue(e.target.value)} />
      <ChildComponent value={count} />
    </div>
  );
}
  • 作用:允许组件在props没有改变的情况下跳过重新渲染。
  • 组件默认渲染机制及优化方式
    • 默认顶层组件重新渲染时,子组件都会重新渲染。
    • 使用React.memo包裹组件,只有props发生变化时才重新渲染。
    • 对于props的比较是浅比较,针对对象数据类型对比引用是否相等,可自定义比较函数实现深度比较。

3. useCallback

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

const ChildComponent = React.memo(function Child({ onButtonClick }) {
  console.log('子组件渲染');
  return <button onClick={onButtonClick}>点击我</button>;
});

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

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

  return (
    <div>
      <ChildComponent onButtonClick={handleButtonClick} />
      <p>计数: {count}</p>
    </div>
  );
}
  • 作用:缓存回调函数,使函数在组件渲染时保持引用稳定。
  • 使用场景及优化方式
    • 当给子组件传递回调函数类型的prop且希望在父组件重新渲染时子组件不重新渲染时使用。
    • 将回调函数用useCallback包裹,并传入依赖项数组,只有依赖项变化时回调函数引用才会改变。

三、forwardRef + useImperativeHandle

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

const MyInputComponent = forwardRef((props, ref) => {
  const inputRef = useRef(null);

  const focusInput = () => {
    inputRef.current.focus();
  };

  useImperativeHandle(ref, () => ({
    focus: focusInput
  }));

  return <input type="text" ref={inputRef} {...props} />;
});

function App() {
  const myInputRef = useRef(null);

  const handleFocus = () => {
    myInputRef.current.focus();
  };

  return (
    <div>
      <MyInputComponent ref={myInputRef} />
      <button onClick={handleFocus}>聚焦输入框</button>
    </div>
  );
}

1. forwardRef

  • 作用:允许组件使用ref将一个 DOM 节点暴露给父组件。

2. useImperativeHandle

  • 作用:如果不想暴露子组件中的 DOM 而是想暴露子组件内部的方法,可使用该函数。

四、Class API

import React, { Component } from 'react';

class Counter extends Component {
  constructor(props) {
    super(props);
    this.state = {
      count: 0
    };
  }

  incrementCount = () => {
    this.setState(prevState => ({ count: prevState.count + 1 }));
  }

  render() {
    return (
      <div>
        <p>计数: {this.state.count}</p>
        <button onClick={this.incrementCount}>增加计数</button>
      </div>
    );
  }
}

function App() {
  return (
    <div>
      <Counter />
    </div>
  );
}

1. 基础体验

  • 使用 ES6 原生 Class API 编写 React 组件,包含状态变量定义、事件回调定义和 UI 模版渲染。

2. 组件生命周期

3. 组件通信

  • 父传子:通过props传递数据。
  • 子传父:子组件通过调用父组件传递的回调函数传递数据。

五、zustand

1. 快速上手

// store.js
import { create } from 'zustand';

const useStore = create(set => ({
  count: 0,
  increment: () => set(state => ({ count: state.count + 1 }))
}));

// App.js
import React from 'react';
import useStore from './store';

function App() {
  const { count, increment } = useStore();

  return (
    <div>
      <p>计数: {count}</p>
      <button onClick={increment}>增加计数</button>
    </div>
  );
}
  • 创建 store:使用create函数创建store,定义状态和相关操作函数(如修改状态的函数)。
  • 绑定组件:在组件中使用useStore获取store中的状态和操作函数,并在视图中使用。

2. 异步支持

// store.js
import { create } from 'zustand';

const useStore = create(set => ({
  data: null,
  fetchData: async () => {
    const response = await fetch('https://example.com/api/data');
    const jsonData = await response.json();
    set({ data: jsonData });
  }
}));

// App.js
import React, { useEffect } from 'react';
import useStore from './store';

function App() {
  const { data, fetchData } = useStore();

  useEffect(() => {
    fetchData();
  }, [fetchData]);

  return (
    <div>
      {data? <pre>{JSON.stringify(data, null, 2)}</pre> : <p>正在加载数据...</p>}
    </div>
  );
}
  • 直接在store的操作函数中编写异步逻辑,将获取的数据通过set函数更新store状态。

3. 切片模式

// counterSlice.js
import { create } from 'zustand';

const createCounterSlice = set => ({
  count: 0,
  increment: () => set(state => ({ count: state.count + 1 }))
});

// userSlice.js
import { create } from 'zustand';

const createUserSlice = set => ({
  user: null,
  setUser: userData => set({ user: userData })
});

// store.js
import { create } from 'zustand';
import { createCounterSlice, createUserSlice } from './slices';

const useStore = create(set => ({
 ...createCounterSlice(set),
 ...createUserSlice(set)
}));

// App.js
import React from 'react';
import useStore from './store';

function App() {
  const { count, increment, user, setUser } = useStore();

  return (
    <div>
      <p>计数: {count}</p>
      <button onClick={increment}>增加计数</button>
      <p>用户信息: {user? JSON.stringify(user) : '未登录'}</p>
      <button onClick={() => setUser({ name: 'John', age: 30 })}>设置用户信息</button>
    </div>
  );
  • store较大时,可将其拆分成多个切片(每个切片定义相关的状态和操作),然后组合这些切片创建最终的store

4. 对接 DevTools

  • 安装simple-zustand-devtools调试工具,在开发环境下配置并使用该工具调试store状态变化。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值