【React】03-关于React state(状态) 的实践

背景

在学习React组件的过程中,发现state的运用很广泛,但对于它的使用及运行机制还是比较模凌两可的,故找了一些资料学习一下。

实践

React中的组件类型被分为了两类:函数组件,又被称为无状态组件;类组件,又被称为有状态组件。状态(state)即数据。函数组件没有自己的状态,自负责数据展示。类组件有自己的状态,负责更新UI。React中想要实现该功能,就要使用类组件(有状态组件)。随着React的发展,在React 16.8之后,引入了Hooks,函数组件也可以开始使用状态,如下例子:

// 函数式组件中使用 useState Hook
import React, { useState } from 'react';

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

  return (
    <div>
      <p>You clicked {count} times</p>
      <button onClick={() => setCount(count + 1)}>
        Click me
      </button>
    </div>
  );
}

而对于有状态组件(类组件),我们通常在constructor中定义state,通过this.setState()去修改,react会自动帮我们合并state中未修改的部分。

import React from "react";


class Clock extends React.Component {
    constructor(props) {
        super(props);
        this.timer = null;
        this.state = {
          currentTime: new Date()
        };
      }
    
      componentDidMount() {
        this.timer = setInterval(() => {
          this.setState({
            currentTime: new Date()
          });
        }, 5000);
      }
    
      componentWillUnmount() {
        clearInterval(this.timer);
      }
    
      render() {
        const { currentTime } = this.state;
        return (
          <div>
            <p>currentTime: {currentTime.toLocaleTimeString()}</p>
          </div>
        );
      }
  }

  export default Clock;

注意! 当我们调用this.setState(),组件会调用render方法重新渲染页面而直接去修改state,而直接修改不会重新渲染组件,在官方文档描述里也不推荐这种做法。因为它可能导致 React 无法正确地进行状态追踪和组件的重新渲染。

另一方面,state的更新可能会被合并,当你连续多次调用this.setState()的时候,出于性能考虑,react会将多次合并为一次更新,这导致了state的更新可能是异步的,所以当我们修改state的时候不应该依赖旧的state(为了解决这个问题,setState也支持传入一个函数接受最新的props和state)。

// 错误姿势
this.setState({
  counter: this.state.counter + this.props.increment,
});
// 正确姿势
this.setState((state, props) => ({
  counter: state.counter + props.increment
}));

最佳实践

这里主要罗列了React社区提出了一些最佳实践方式:

  1. 状态提升(Lifting State Up): 当多个组件共享状态时,将状态提升到这些组件的最近共同父组件是一种良好的实践。这样可以避免状态的不一致性和复杂性。

    例如一个简单的购物车应用。我们将创建两个组件:ProductListShoppingCartProductList 显示可供购买的商品列表,而 ShoppingCart 显示已选商品和计算总价。我们将使用状态提升来管理选购商品的状态。那么我们可以考虑如下代码:
    在这个例子中,ProductList 组件管理商品列表和已选商品的状态。当用户点击 “Add to Cart” 按钮时,通过回调函数 handleProductSelect 更新状态,并将选购的商品传递给 ShoppingCart 组件。通过这种方式,ShoppingCart 组件通过 props 获取所需的状态,并显示已选商品和计算总价。这样两个组件通过 state 提升的方式共享了选购商品的状态。

// 商品列表组件
class ProductList extends React.Component {
  state = {
    products: [
      { id: 1, name: 'Product A', price: 20 },
      { id: 2, name: 'Product B', price: 30 },
      { id: 3, name: 'Product C', price: 40 }
    ],
    selectedProducts: []
  };

  handleProductSelect = (productId) => {
    const selectedProduct = this.state.products.find(product => product.id === productId);
    this.setState(prevState => ({
      selectedProducts: [...prevState.selectedProducts, selectedProduct]
    }));
  }

  render() {
    return (
      <div>
        <h2>Product List</h2>
        <ul>
          {this.state.products.map(product => (
            <li key={product.id}>
              {product.name} - ${product.price}
              <button onClick={() => this.handleProductSelect(product.id)}>Add to Cart</button>
            </li>
          ))}
        </ul>
        <ShoppingCart selectedProducts={this.state.selectedProducts} />
      </div>
    );
  }
}

// 购物车组件
class ShoppingCart extends React.Component {
  calculateTotalPrice = () => {
    return this.props.selectedProducts.reduce((total, product) => total + product.price, 0);
  }

  render() {
    return (
      <div>
        <h2>Shopping Cart</h2>
        <ul>
          {this.props.selectedProducts.map(product => (
            <li key={product.id}>
              {product.name} - ${product.price}
            </li>
          ))}
        </ul>
        <p>Total Price: ${this.calculateTotalPrice()}</p>
      </div>
    );
  }
}

// 应用根组件
class App extends React.Component {
  render() {
    return (
      <div>
        <ProductList />
      </div>
    );
  }
}

  1. 用状态容器库: 对于大型应用,可以考虑使用状态容器库(如Redux、MobX)来集中管理应用的状态。这样可以更好地组织和分离关注点,使得应用的状态变得更加可维护。

    关于状态容器库,其实就像Java里封装好的函数方法或者jar包。在前端开发中,状态容器通常用于管理全局状态,使得多个组件能够方便地访问和共享相同的状态数据。以Redux为例子,它通过单一的不可变状态树来管理整个应用的状态,并使用纯函数来执行状态的变更。

你可能感兴趣的:(前端,react.js,javascript,前端)