< 关於 React: 开始打地基| useEffect() >

09-09-2021

本章内容
  • function component effects
    • 创立一个弹跳视窗
  • Clean Up Effects
  • 当effect 被呼叫时的控制方式
  • Fetch Data时的用法
  • Hooks 的规则
  • 独立使用每一个Hook与Effects
  • useEffect 回顾

Function Component Effects

使用时在component中需把useEffectreact函式库中引入

import { useEffect } from 'react';

若需要与其他的变数联合起来需要需要写成:

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

使用useEffect()创立一个弹跳视窗

我们的effect在function component渲染後被调用,但仍然可以使用function component中的变数。
当React 渲染function component後,会一样的更新DOM,然後在DOM更新後运行我们的效果。

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

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

  useEffect(() => {
    alert(`Count: ${count}`);
  });

  const handleClick = () => {
    setCount((prevCount) =>  prevCount + 1);
  };

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

一定要在调用State Hook之後使用Effect Hook,这样我们才能使用到count变数和之前的handleClick()函数。

useEffect(() => {
    alert(`Count: ${count}`);
  });

使用useEffect()调用
alert()使用(Template literals)反引号(back-tick)将显示的变数写在里面

Clean Up Effects

若没有将effect return clean up ,那每次document 在component 重新渲染时都会将一个新的事件监听气添加到DOM中,不仅会导致错误,也会让效能下降

  • 解决方式:因为effect()都是在渲染之後执行,而且不是一次,是每次React 重新渲染时都会执行。所以我们在渲染之前和卸载之後使用clean up effect清理每一个调用的效果

若我们的effect retrun是一个function,那use effect() 就会把他当做是一个 cleanup function。 React 会在重新渲染之前或是卸载之後呼叫cleanup function 。

  • cleanup function 是非必需的,但我们避免产生memory leak 所以要清除掉

* memory leak
(记忆体管理)
当value在被宣告时同时也完成了记忆体的配置,且会自动释放不再使用的值。

生命周期:
  1. 配置程序需要的记忆体
  2. 使用配置到的记忆体空间(读、写)
  3. 不自使用时释放已经被配置的记忆体空间

回收的机制主要是「参考概念」,如果物件中会使用到另外一个物件,即是该物件参考另外一个物件。
JavaScript 中的proptotype以及该物件的属性,即是隐式参考(前者)以及显示参考(後者)

Reference-counting garbage collection
可以用「没有其他物件参考它」简言之,如果一个物件没有被其他物件参考,即可被视为可以被回收的记忆体垃圾。

// 范例
import React, { useState, useEffect } from 'react';

export default function Counter() {
  const [clickCount, setClickCount] = useState(0);

  const increment = () => setClickCount((prev) => prev + 1);

  useEffect(() => {
    document.addEventListener('mousedown', increment);
    return () => {
      document.removeEventListener('mousedown', increment);
    };
  });

  return (
      <h1>Document Clicks: {clickCount}</h1>
  );
}

监听滑鼠按下的监听事件,cleanup function 是一个新的function内容在effect中return

useEffect(() => {
    document.addEventListener('mousedown', increment);
    return () => {
      document.removeEventListener('mousedown', increment);
    };
  });

当effect 被呼叫时的控制方式

在定义function component时,使用effect 通常只有在component在mounts时候(renders 的第一次),而不是在re-render的时候,而Effect Hook 可以让我们达成这件事情。

如果我们想要在第一次渲染後调用effect,我们传递一个空的array给useEffect()作为第二个参数。
而这个第二个参数称为==依赖阵列(dependency array)==
依赖阵列告诉useEffect()何时该调用effect何时该跳过它。

effect 总是会在第一次渲染後调用 ; 但只有在依赖阵列中的某些内容渲染间更改了value才会再次调用

// 范例
useEffect(() => {
  document.title = `You clicked ${count} times`;
}, [count]); // 只会在count的值发生变化时再次调用
  • 依赖阵列、第一次渲染後调用的效果:
    不明确时:每次重新渲染
    空阵列时:不重新渲染
    非空阵列时:当依赖的阵列中的任何值发生变化

Fetch Data时的用法

当component 渲染的数据没有变化时,我们可以传递一个空的阵列(依赖阵列),在第一次渲染後获得数据,当收到来自服务器的响应时,我们可以使用 State Hook 中的setState()将来自服务器响应的数据储存在我们本地组件状态中以供渲染。这种方式一起使用 State Hook 和 Effect Hook,可以避免我们的component在每次渲染後不必要地获取新数据!

一个非空的依赖阵列向 Effect Hook 发出请求时,它可以在重新渲染後跳过调用我们的效果,除非我们的依赖阵列中的一个变数的value发生了变化。如果依赖项的值发生了变化,那麽 Effect Hook 会再次调用我们的 effect!

import React, { useState, useEffect } from 'react';
import { get } from './Backend/fetch';

export default function Forecast() {
  const [data, setData] = useState(null);
  const [file, setFile] = useState({});
  const [fileList, setFileList] = useState('/korea');

  useEffect(() => {
    alert('资料来了···');
    get(fileList).then((response) => {
      alert('Response: ' + JSON.stringify(response,'',2));
      setData(response.data);
    });
  }, [fileList]);

  const handleChange = (itemId) => ({ target }) =>
    setFile((prev) => ({
      ...prev,
      [itemId]: target.value
    }));

  if (!data) {
    return <p>Loading...</p>;
  }

  return (
    <div className='App'>
      <h1>影片区</h1>
      <div>
        <button onClick={() => setFileList('/korea')}>韩剧馆</button>
        <button onClick={() => setFileList('/japan')}>日剧馆</button>
      </div>
    </div>
  );
}

使用 if 在没有接到数据时渲染出Loading..


if (!data) {
    return <p>Loading...</p>;
}

将接到的数据存在setData()之中
使用useEffect的第二个参数,一个空的阵列,确保我们的component 在第一次渲染之後才拿到数据

  useEffect(() => {
    alert('资料来了···');
    get(fileList).then((response) => {
      alert('Response: ' + JSON.stringify(response,'',2));
      setData(response.data);
    });
  }, [fileList]);

将fetch 的api 整理成独立一份文件,使用get变数引入

在方法中使用变数forecastType的值来确定要去调用哪个端点的资料,如此只要值有改变就会决定要去调用哪一个端点/korea \ /japan

import { get } from './Backend/fetch';

  useEffect(() => {
    alert('资料来了···');
    get(fileList).then((response) => {
      alert('Response: ' + JSON.stringify(response,'',2));
      setData(response.data);
    });
  }, [fileList]);

//
return 
button onClick={() => setFileList('/korea')}>韩剧馆</button>

Hooks 的规则

  1. 只能在top level调用Hooks
  2. 只从React 函数中调用Hooks
  3. 只在React function 中使用,不能在class function中使用
  4. 不能在一般JavaScript函数中使用
  5. 自定义的Hook中使用

错误示范:

if (userName !== '') {
  useEffect(() => {
    localStorage.setItem('savedUserName', userName);
  });
}

正确示范


useEffect(() => {
  if (userName !== '') {
    localStorage.setItem('savedUserName', userName);
  }
});

独立使用每一个Hook与Effects

将数据分门管理取代只有一个data下的使用方式,由於众多资料都归在同一个data上,不仅会造成阅读上的障碍,也使得使用effect上充满冲突,将data内使用到的项目分门别类的设置 useState()管理,再将从data中取得的资料储存到setState以供後续提用。

import React, { useState, useEffect } from 'react';
import { get } from './mockBackend/fetch';

export default function SocialNetwork() {
  const [menu, setMenu] = useState(null);
  useEffect(() => {
    get('/menu').then((response) => {
      setMenu(response.data);
    });
  }, []);
// 更多其他引入的data省略..
  return (
    <div className='App'>
      <h1>My Network</h1>
      {!menu ? (
        <p>Loading..</p>
      ) : (
        <nav>
          {menu.map((menuItem) => (
            <button key={menuItem}>{menuItem}</button>
          ))}
        </nav>
      )}
    // 更多资料呈现省略..
    </div>
  );
}

在这里使用了三元方程序判别接取到data的情境,以及使用menu做.map的方式将资料捞出显示。

{!menu ? (
        <p>Loading..</p>
      ) : (
        <nav>
          {menu.map((menuItem) => (
            <button key={menuItem}>{menuItem}</button>
          ))}
        </nav>
      )}

useEffect 回顾

  1. 从react库中导入这个函数并在function component中调用
  2. effect 指的是function当作参数传递的useEffect()函数,默认在每次渲染後调用此效果
  3. cleanup function 可选择return的function,如果effect做了需要清理防止memory leek的事情,effect会返回一个cleanup ,那麽effect hook 就会再次调用effect之前以及在卸载component时调用cleanup function
  4. dependency array: 这是可选的第二个参数,可以使用useEffect()使用参数调用函式,防止在不需要时重复调用,阵列应该要包含effect 所依赖的变数。

<<:  Day9 盒模型

>>:  DevOps在MLOps当中的角色

JavaScript Day 9. if、else if、if包if

if 当条件成立的时候会执行 if 陈述式里的程序,而不成立时则执行另外一个陈述式。if 单从字面上...

.NET Core第21天_FormTagHelper的使用_防止跨站请求设置方式

FormTagHelper : 为.net对html原生的封装, 预设若没指定method则是采用g...

Day09 X Resource Hint & Non-Blocking Script Tag

经过昨天的内容,读者们应该对於网页的渲染流程有大致的理解了。 我们再小小复习一下,大致上网页的渲染...

【後转前要多久】# Day02 HTML - 基本观念复习

基本上网页与HTML、CSS、JavaScript息息相关, 虽然有办法、但很难去完整地分别拆开来讲...

Day 30 整合宝石:【Lab】建构三层式云端架构 (EC2+VPC+S3+RDS+IAM) (下)

本文将继上篇 【Lab】建构三层式云端架构(上),实作下半部分的内容,包含档案控管、RDS/EC2...