帮助
支持我们

副作用

副作用是在虚拟 DOM 树中发生变化时运行的代码片段。它们不遵循接受 props 并返回新的虚拟 DOM 树的标准方法,并且经常超出树范围来改变状态或调用命令式代码,例如调用 DOM API。副作用也经常用作触发数据获取的方法。

效果:函数组件中的副作用

在学习 refs 和 useRef() 钩子时,我们已经在上一章中看到一个副作用的示例。一旦我们的 ref 使用指向 DOM 元素的 current 属性填充,我们就需要一种方法来“触发”将与该元素交互的代码。

为了在渲染后触发代码,我们使用了 useEffect() 钩子,这是从函数组件创建副作用的最常见方式

import { useRef, useEffect } from 'preact/hooks';

export default function App() {
  const input = useRef()

  // the callback here will run after <App> is rendered:
  useEffect(() => {
    // access the associated DOM element:
    input.current.focus()
  }, [])

  return <input ref={input} />
}

请注意,将空数组作为第二个参数传递给 useEffect()。当“依赖项”数组中的任何值从一次渲染更改为下一次渲染时,效果回调将运行。例如,当组件第一次渲染时,所有效果回调都将运行,因为没有先前的“依赖项”数组值可供比较。

我们可以向“依赖项”数组中添加值,以根据条件触发效果回调,而不仅仅是在组件首次渲染时触发。这通常用于在响应数据更改或组件从页面中移除(“卸载”)时运行代码。

让我们看一个示例

import { useEffect, useState } from 'preact/hooks';

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

  useEffect(() => {
    console.log('<App> was just rendered for the first time')
  }, [])

  useEffect(() => {
    console.log('count value was changed to: ', count)
  }, [count])
  //  ^ run this any time `count` changes, and on the first render

  return <button onClick={() => setCount(count+1)}>{count}</button>
}

生命周期方法:类组件副作用

类组件还可以通过实现 Preact 提供的任何可用 生命周期方法 来定义副作用。以下是一些最常用的生命周期方法

生命周期方法运行时间
componentWillMount组件首次渲染之前
componentDidMount组件首次渲染之后
componentWillReceiveProps组件重新渲染之前
componentDidUpdate组件重新渲染之后

在类组件中使用副作用的最常见示例之一是在组件首次渲染时获取数据,然后将该数据存储在状态中。以下示例显示了一个组件,它在首次渲染后从 JSON API 请求用户信息,然后显示该信息。

import { Component } from 'preact';

export default class App extends Component {
  // this gets called after the component is first rendered:
  componentDidMount() {
    // get JSON user info, store in `state.user`:
    fetch('/api/user')
      .then(response => response.json())
      .then(user => {
        this.setState({ user })
      })
  }

  render(props, state) {
    const { user } = state;

    // if we haven't received data yet, show a loading indicator:
    if (!user) return <div>Loading...</div>

    // we have data! show the username we got back from the API:
    return (
      <div>
        <h2>Hello, {user.username}!</h2>
      </div>
    )
  }
}

试试看!

我们将保持这个练习的简单性:更改右侧的代码示例,以便在每次 count 更改时记录日志,而不仅仅是在首次渲染 <App> 时记录日志。

正在加载...