react组件(2)---State 与生命周期

1. 什么是 React State?

State(状态)是 React 组件中存储可变数据的容器,它决定了组件的行为和渲染输出。与 Props 不同,State 是组件内部管理且可以变化的,而 Props 是从父组件传递过来的只读属性。

State 的核心特征包括:

  • 可变性:State 可以在组件生命周期内发生变化
  • 响应式:State 的改变会自动触发组件的重新渲染
  • 局部性:State 是组件私有的,其他组件无法直接访问
  • 异步性:setState 操作可能是异步的,React 会批量处理状态更新

在 React 中,将组件视为一个状态机,通过管理状态的变化来驱动 UI 的更新。

2. 类组件中的 State

2.1 状态的声明与初始化

在类组件中,State 通常在构造函数中初始化:

scala 复制代码
class Counter extends React.Component {
  constructor(props) {
    super(props);
    this.state = {
      count: 0,
      isActive: false
    };
  }
}

现代写法也可以使用类属性语法:

scala 复制代码
class Counter extends React.Component {
  state = {
    count: 0,
    isActive: false
  };
}

2.2 状态的更新

更新 State 必须使用 setState()方法,绝对不能直接修改 state

kotlin 复制代码
// 错误做法
this.state.count = 1; // 不会触发重新渲染!

// 正确做法
this.setState({ count: 1 });

// 当新状态依赖于旧状态时,使用函数形式
this.setState(prevState => ({
  count: prevState.count + 1
}));

3. 函数组件中的 State(useState Hook)

React 16.8 引入了 Hooks,允许函数组件使用 State。useState是最基础的 Hook。

3.1 useState 的基本用法

javascript 复制代码
import React, { useState } from 'react';

function Counter() {
  const [count, setCount] = useState(0);
  
  return (
    <div>
      <p>当前计数:{count}</p>
      <button onClick={() => setCount(count + 1)}>
        点击增加
      </button>
    </div>
  );
}

3.2 useState 的高级用法

函数式更新(当新状态依赖于旧状态时):

ini 复制代码
const [count, setCount] = useState(0);

// 推荐:使用函数式更新确保基于最新状态
const increment = () => {
  setCount(prevCount => prevCount + 1);
};

延迟初始化(当初始状态需要复杂计算时):

ini 复制代码
const [state, setState] = useState(() => {
  const expensiveValue = performExpensiveCalculation();
  return expensiveValue;
});

对象和数组的状态管理

ini 复制代码
// 对象状态更新
const [user, setUser] = useState({ name: 'Alice', age: 25 });
setUser(prevUser => ({ ...prevUser, name: 'Bob' }));

// 数组状态更新
const [items, setItems] = useState(['apple', 'banana']);
setItems(prevItems => [...prevItems, 'orange']);

4. React 组件生命周期

类组件生命周期 函数组件 useEffect 实现方式
componentDidMount useEffect (() => {}, [])(空依赖数组)
componentDidUpdate useEffect (() => {}, [deps])(依赖项变化时执行)
componentWillUnmount useEffect (() => { return () => {} }, [])(返回清理函数)

4.1 生命周期概述

React 组件的生命周期可以分为三个主要阶段:

  • 挂载阶段:组件被创建并插入 DOM
  • 更新阶段:组件的 props 或 state 发生变化时重新渲染
  • 卸载阶段:组件从 DOM 中移除

4.2 类组件的生命周期方法

挂载阶段

  • constructor():初始化 state 和绑定方法
  • static getDerivedStateFromProps():在渲染前根据 props 更新 state
  • render():渲染组件(必需方法)
  • componentDidMount():组件挂载后执行,适合进行数据获取、订阅等副作用操作

更新阶段

  • static getDerivedStateFromProps():更新前根据 props 调整 state
  • shouldComponentUpdate():决定组件是否应该更新(性能优化关键)
  • render():重新渲染组件
  • getSnapshotBeforeUpdate():在 DOM 更新前捕获一些信息
  • componentDidUpdate():组件更新后执行,可以操作 DOM 或执行网络请求

卸载阶段

  • componentWillUnmount():组件卸载前执行清理操作,如取消定时器、网络请求等

4.3 函数组件中的"生命周期"(useEffect Hook)

useEffectHook 在函数组件中承担了生命周期方法的职责:

javascript 复制代码
import React, { useState, useEffect } from 'react';

function Example() {
  const [count, setCount] = useState(0);
  
  // 相当于 componentDidMount + componentDidUpdate
  useEffect(() => {
    document.title = `点击了 ${count} 次`;
  });
  
  // 只在挂载时执行(类似 componentDidMount)
  useEffect(() => {
    console.log('组件挂载完成');
  }, []);
  
  // 依赖变化时执行(类似 componentDidUpdate)
  useEffect(() => {
    console.log(`count 变为: ${count}`);
  }, [count]);
  
  // 清理效果(类似 componentWillUnmount)
  useEffect(() => {
    const timer = setInterval(() => {
      // 一些操作
    }, 1000);
    
    return () => {
      clearInterval(timer); // 清理函数
    };
  }, []);
  
  return (
    <div>
      <p>你点击了 {count} 次</p>
      <button onClick={() => setCount(count + 1)}>
        点击我
      </button>
    </div>
  );
}

5. State 设计的最佳实践

5.1 State 的最小化原则

只将真正需要响应式更新的数据放入 State,派生数据可以在渲染时计算:

csharp 复制代码
// 不好的做法:将派生数据放入 State
state = {
  items: [],
  totalCount: 0, // 可以从 items.length 派生
  filteredItems: [] // 可以从 items 过滤得到
};

// 好的做法:只存储原始数据
state = {
  items: []
};

// 派生数据在 render 中计算
get totalCount() {
  return this.state.items.length;
}

5.2 State 结构的扁平化

避免嵌套过深的 State 结构:

css 复制代码
// 不好的嵌套结构
state = {
  user: {
    profile: {
      personalInfo: {
        name: '',
        age: 0
      }
    }
  }
};

// 好的扁平结构
state = {
  userName: '',
  userAge: 0
};

5.3 不可变更新模式

始终使用不可变的方式更新 State:

kotlin 复制代码
// 数组更新
// 错误:直接修改原数组
this.state.items.push(newItem);
// 正确:创建新数组
this.setState({
  items: [...this.state.items, newItem]
});

// 对象更新
// 错误:直接修改原对象
this.state.user.name = 'New Name';
// 正确:创建新对象
this.setState({
  user: { ...this.state.user, name: 'New Name' }
});

6. 常见场景与实战示例

6.1 数据获取场景

kotlin 复制代码
class DataFetcher extends React.Component {
  state = {
    data: [],
    loading: true,
    error: null,
    page: 1
  };
  
  componentDidMount() {
    this.fetchData();
  }
  
  componentDidUpdate(prevProps, prevState) {
    if (prevState.page !== this.state.page) {
      this.fetchData();
    }
  }
  
  fetchData = async () => {
    try {
      this.setState({ loading: true, error: null });
      const response = await fetch(`/api/data?page=${this.state.page}`);
      const result = await response.json();
      this.setState({ data: result, loading: false });
    } catch (error) {
      this.setState({ error: error.message, loading: false });
    }
  };
  
  render() {
    const { data, loading, error, page } = this.state;
    
    if (loading) return <div>加载中...</div>;
    if (error) return <div>错误: {error}</div>;
    
    return (
      <div>
        {/* 渲染数据 */}
      </div>
    );
  }
}

6.2 表单处理场景

ini 复制代码
function ContactForm() {
  const [formData, setFormData] = useState({
    name: '',
    email: '',
    message: ''
  });
  
  const handleChange = (e) => {
    const { name, value } = e.target;
    setFormData(prev => ({
      ...prev,
      [name]: value
    }));
  };
  
  const handleSubmit = (e) => {
    e.preventDefault();
    // 提交表单数据
  };
  
  return (
    <form onSubmit={handleSubmit}>
      <input
        name="name"
        value={formData.name}
        onChange={handleChange}
      />
      <input
        name="email"
        value={formData.email}
        onChange={handleChange}
      />
      <textarea
        name="message"
        value={formData.message}
        onChange={handleChange}
      />
      <button type="submit">提交</button>
    </form>
  );
}

7. 常见陷阱与解决方案

7.1 过时状态问题

在闭包中捕获过时状态值:

scss 复制代码
// 问题代码:可能捕获过时状态
const [count, setCount] = useState(0);
const increment = () => {
  setTimeout(() => {
    setCount(count + 1); // 可能使用过时的 count 值
  }, 3000);
};

// 解决方案:使用函数式更新
const increment = () => {
  setTimeout(() => {
    setCount(prevCount => prevCount + 1); // 总是基于最新状态
  }, 3000);
};

7.2 useEffect 的依赖数组

正确处理 useEffect 的依赖数组避免无限循环:

scss 复制代码
// 错误:缺少依赖可能导致过时数据
useEffect(() => {
  fetchData(userId);
}, []);

// 错误:依赖不完整可能导致意外行为
useEffect(() => {
  fetchData(userId);
}, [userId]); // 如果 fetchData 使用了其他状态,需要包含

// 正确:包含所有依赖
useEffect(() => {
  fetchData(userId);
}, [userId, fetchData]); // 如果 fetchData 在渲染中定义,需要用 useCallback 包装

8. 总结

React 的 State 和生命周期是构建交互式界面的核心概念。无论是类组件还是函数组件,合理管理状态和理解组件生命周期都是开发高质量 React 应用的关键。

主要要点回顾

  • State 是组件内部的可变数据,Props 是从外部传入的只读数据
  • 类组件使用 this.statethis.setState(),函数组件使用 useStateHook
  • 生命周期方法让你在组件不同阶段执行代码,useEffect在函数组件中承担类似职责
  • 遵循 State 设计最佳实践(最小化、扁平化、不可变更新)
  • 注意常见陷阱,如过时状态和 useEffect 的依赖处理

随着 React 的发展,函数组件和 Hooks 已成为主流,但理解类组件的生命周期对于维护现有项目和深入理解 React 原理仍然很有价值。

希望本篇博客能帮助你更好地理解和应用 React 的 State 与生命周期概念!

相关推荐
咕噜企业分发小米2 小时前
如何平衡服务器内存使用率和系统稳定性?
java·服务器·前端
GoldenPlayer2 小时前
Web-Tech:CORS的触发机制
前端
AY呀2 小时前
Vite:现代前端构建工具的革命与实战指南
前端·vue.js·vite
爬山算法2 小时前
Netty(13)Netty中的事件和回调机制
java·前端·算法
前端无涯2 小时前
react组件(3)---组件间的通信
前端·react.js
前端无涯2 小时前
react组件(1)---从入门到上手
react.js·前端框架
讯方洋哥3 小时前
应用冷启动优化
前端·harmonyos
speedoooo3 小时前
未来的App不再需要菜单栏?
前端·ui·容器·小程序·web app
猿究院_xyz3 小时前
微信小程序与echarts联动安卓真机测试出现黑色阴影
前端·javascript·微信小程序·小程序·echarts