sfsoul / personalBlog

思考与总结
MIT License
1 stars 0 forks source link

React FAQ #26

Open sfsoul opened 3 years ago

sfsoul commented 3 years ago

应该在 React 组件的哪个生命周期函数中发起 Ajax 请求?

推荐在 componentDIdMount 这个生命周期函数中发起 Ajax 请求,这样可以拿到 Ajax 请求返回的数据并通过 setState 来更新组件。

// 示例 API 返回如下的 JSON 对象:
{
  "items": [
    { "id": 1, "name": "Apples",  "price": "$2" },
    { "id": 2, "name": "Peaches", "price": "$5" }
  ] 
}
import { useState, useEffect } from 'React'

function MyComponent() {
    const [error, setError] = useState(null);
    const [isLoaded, setIsLoaded] = useState(false);
    const [items, setItems] = useState([]);

    useEffect(() => {
        fetch("https://api.example.com/items")
            .then(res => res.json())
            .then(result => {
                setIsLoaded(true);
                setItems(result);
            }, (error) => {
                setIsLoaded(true);
                setItems(error);
            })
    }, [])

    if (error) {
        return <div>Error: {error.message}</div>;
    } else if (!isLoaded) {
        return <div>Loading...</div>
    } else {
        return (
            <ul>
                {items.map(item => (
                    <li key={item.name}>
                        {item.name} {item.price}
                    </li>
                ))}
            </ul>
        );
    }
}

怎样阻止函数被调用太快或者太多次?

若有一个 onClick 或者 onScroll 这样的事件处理器,想要阻止回调被触发的太快,则可以限制执行回调的速度,可通过以下几种方式做到:

_.debounce 、_.throttle 和 raf-schd 都提供了一个 cancel 方法来取消延迟回调。需要在 componentWillUnmount 中调用该方法,或者对代码进行检查来保证在延迟函数有效期间内组件始终挂载。

节流

节流阻止函数在给定时间窗口内被调不能超过一次。

import throttle from 'lodash.throttle';

class LoadMoreButton extends React.Component {
  constructor(props) {
    super(props);
    this.handleClick = this.handleClick.bind(this);
    this.handleClickThrottled = throttle(this.handleClick, 1000);
  }

  componentWillUnmount() {
    this.handleClickThrottled.cancel();
  }

  render() {
    return <button onClick={this.handleClickThrottled}>Load More</button>;
  }

  handleClick() {
    this.props.loadMore();
  }
}
sfsoul commented 3 years ago

组件状态

setState 实际做了什么?

setState() 会对一个组件的 state 对象安排一次更新。当 state 改变了,该组件会重新渲染。

state 和 props 之间的区别是什么?

它们都是用来保存信息的,这些信息可以控制组件的渲染输出,而它们的一个重要的不同点是:props 是传递给组件的(类似于函数的形参),而 state 是在组件内被组件自己管理的(类似于在一个函数内声明的变量)。

为什么 setState 给了我一个错误的值?

调用 setState 是异步的 --- 不要指望在调用 setState 后,this.state 会立即映射为新的值。若需要基于当前的 state 来计算出新的值,应该传递一个函数,而不是一个对象。

如何更新那些依赖于当前的 state 的 state 呢?

setState 传递一个函数,而不是一个对象,就可以确保每次的调用都是使用最新版的 state。

给 setState 传递一个对象与传递一个函数的区别是什么?

传递一个函数可以让你在函数内访问到当前的 state 的值。因为 setState 的调用是分批的,所以可以链式地进行更新,并确保它们是一个建立在另一个之上的,这样就不会发生冲突:

incrementCount() {
  this.setState((state) => {
    // 重要:在更新的时候读取 `state`,而不是 `this.state`。
    return {count: state.count + 1}
  });
}

handleSomething() {
  // 假设 `this.state.count` 从 0 开始。
  this.incrementCount();
  this.incrementCount();
  this.incrementCount();

  // 如果你现在在这里读取 `this.state.count`,它还是会为 0。
  // 但是,当 React 重新渲染该组件时,它会变为 3。
}
sfsoul commented 3 years ago

传递函数给组件

如何为函数绑定组件实例?

在构造函数中绑定:

class Foo extends Component {
  constructor(props) {
    super(props);
    this.handleClick = this.handleClick.bind(this);
  }
  handleClick() {
    console.log('Click happened');
  }
  render() {
    return <button onClick={this.handleClick}>Click Me</button>;
  }
}

class 属性(第三阶段提案)

class Foo extends Component {
  // Note: this syntax is experimental and not standardized yet.
  handleClick = () => {
    console.log('Click happened');
  }
  render() {
    return <button onClick={this.handleClick}>Click Me</button>;
  }
}

在 Render 中的绑定:render 方法中使用 Function.prototype.bind 会在每次组件渲染时创建一个新的函数,可能会影响性能。

  handleClick() {
    console.log('Click happened');
  }
  render() {
    return <button onClick={this.handleClick.bind(this)}>Click Me</button>;
  }
}

在 Render 中使用箭头函数:render 方法中使用箭头函数也会在每次组件渲染时创建一个新的函数。

class Foo extends Component {
  handleClick() {
    console.log('Click happened');
  }
  render() {
    return <button onClick={() => this.handleClick()}>Click Me</button>;
  }
}

如何传递参数给事件处理器或回调?

// 使用箭头函数包裹事件处理器,并传递参数:
<button onClick={() => this.handleClick(id)} />

// 以上代码和调用 .bind 是等价的
<button onClick={this.handleClick.bind(this, id)} />