React 系列 之 React Hooks(一) JSX本质、理解Hooks

news/2024/7/15 18:59:59 标签: react.js, 前端, 前端框架

借鉴自极客时间《React Hooks 核心原理与实战》

JSX语法的本质

可以认为JSX是一种语法糖,允许将html和js代码进行结合。
JSX文件会通过babel编译成js文件
下面有一段JSX代码,实现了一个Counter组件

import React from "react";

export default function Counter() {
  const [count, setCount] = React.useState(0);
  return (
    <div>
      <button onClick={() => setCount(count + 1)}>{count}</button>
    </div>
  );
}

这段代码也可以形成js:

React.createElement(
  "div",
  null,
  React.createElement(
    "button",
    { onClick: function onClick() {
        return setCount(count + 1);
      } },
    React.createElement(CountLabel, { count: count })
  )
);

用到了React的一个api:React.createElement,它的作用是创建组件的实例。这个api的参数:

  • 第一个参数:组件类型,内置组件(对应于html元素)为小写,自定义组件为大写驼峰
  • 第二个参数:为组件添加的属性,即props
  • 第三个及以后的参数:该组件的children

所以呢,通过 createElement 这个 API,我们可以构建出需要的组件树,而 JSX 只是让这种描述变得更加直观和高效。
因此,JSX 不是一个新的概念,而只是原生 JavaScript 的另一种写法。但是换成这种写法,会大大降低你上手 React 的难度。

React组件的本质

从model(state+props)到 view的映射
在这里插入图片描述
React 会帮助你处理所有 DOM 变化的细节。而且,当 Model 中的状态发生变化时,UI 会自动变化,即所谓的数据绑定。
把UI的展现看做是函数的执行

f(变化的model)  => 新的dom树,然后把新的dom树以最优的方法更新到浏览器

为什么要发明 Hooks?

Class组件,有不适合的地方,浪费了类的两个特点:1. 不需要人为调用Class实例的方法,这个Class的特点就浪费了 2. React的类之间没有继承,这个特点也浪费了。
函数组件,也有不合适的地方,1. 没有自己的生命周期,2. 函数自身无法记录状态

我们想要强化函数组件,让它能有状态,让它关联到一个存储在外部的状态,那么Hooks的思想就诞生了

把一个外部的数据绑定到(钩到)函数的执行。
当被钩到的数据或事件发生变化时,产生这个目标结果的代码会重新执行,产生更新后的结果。
对于函数组件,这个结果是最终的 DOM 树

有一点需要特别注意,Hooks 中被钩的对象,不仅可以是某个独立的数据源,也可以是另一个 Hook 执行的结果,这就带来了 Hooks 的最大好处:逻辑的复用。

hooks的过人之处

1 逻辑复用

或者说是简化了逻辑复用
以往的一些逻辑复用,需要借助高阶组件来完成(这是一个复杂的设计模式),但是hooks就能简化这一过程。
举个例子: 调整窗口大小
a. 高阶组件方法
高阶组件实现思路:高阶组件负责监听窗口大小变化,并将变化后的值作为 props 传给下一个组件。

// 组件为参数,最后返回一个包装过的组件
const withWindowSize = Component => {
	// 产生一个高阶组件 WrappedComponent,只包含监听窗口大小的逻辑
	class WrappedComponent extends React.PureComponent {
		constructor(props) {
			super(props);
			this.state = {
			  size: this.getSize() 
			};
		}
		// 组件生命周期对应状态 挂到window的listener上
		componentDidMount() {
		    window.addEventListener("resize", this.handleResize);
		}
		componentWillUnmount() {
			window.removeEventListener("resize", this.handleResize);
		}
		// 获取当前window的size
		getSize() {
			return window.innerWidth > 1000 ? "large" : "small";
		}
		handleResize = () => {
			const currentSize = this.getSize();
			this.setState({
				size: this.getSize()
			});
		}
		render() {
		    // 将窗口大小传递给真正的业务逻辑组件
			return <Component size={this.state.size} />
		}
	}
	return WrappedComponent;
};

然后在自定义组件中调用withWindowSize 这样的函数来产生一个新组件,并自带size属性,例如:

class MyComponent extends React.Component {
	render() {
		const {size} = this.props;
		if(size === 'small') return <SmallComponent />;
		else return <LargeComponent />;
	}
}
// 使用 withWindowSize 产生高阶组件,用于产生 size 属性传递给真正的业务组件
export default withWindowSize(MyComponent)

因为size是props中得到的,即是从父组件传递下来的,所以当WrappedComponent组件中监听到窗口变化时,会更新size的值,从而会让MyComponent重新渲染

高阶组件的缺点:

  1. 代码难理解,不直观,很多人甚至宁愿重复代码,也不愿用高阶组件;
  2. 增加额外的组件节点。每一个高阶组件都会多一层节点,这就会给调试带来很大的负担。

那么用hooks如何实现?
b. hooks方法


const useWindowSize = () => {
  const [size, setSize] = useState(getSize());
  useEffect(() => {
    const handler = () => { setSize(getSize()); }
    window.addEventListener('resize', handler);
    // 回调函数可以返回一个清理函数。这个清理函数在组件卸载时或者在下一次 effect 执行之前执行,
    return () => {
      window.removeEventListener('resize', handler);
    };
  }, []);  // 空的依赖项数组意味着此效果只会运行一次,类似于类组件中的 componentDidMount
  return size;
}

在组件中使用useWindowSize

const Demo = () => {
  const size = useWindowSize();
  if (size == 'small') return <SmallComponent />;
  else return <LargeComponent />;
}

窗口大小是一个外部的数据状态,但我们通过Hooks的方式对其进行了封装,将其变成一个可绑定的数据源。每当窗口大小发生变化时,使用这个Hook的组件就会重新渲染。(当窗口大小改变时,注册的事件监听器会触发,并调用 handler 函数,该函数会更新 size 状态的值。在 Demo 组件中,根据 size 的值来决定渲染 SmallComponent 还是 LargeComponent。当 size 的值在窗口大小变化时被更新后,会触发组件的重新渲染,因为组件的渲染取决于 size 的值)

2 有助于关注分离

Hooks能够将针对同一个业务逻辑的代码尽可能聚合在一块儿,让代码更容易理解和维护,相比之下,Class组件无法做到这一点,因为Class组件中,不得不把同一个业务逻辑的代码分散在类组件的不同生命周期方法中。
举个例子:上面的窗口大小变化监听代码
Class组件中,我们在componentDidMountcomponentWillUnmount中分别去监听事件和解绑事件;
而在函数组件中,就可以把逻辑都集中写在hooks里

Hooks所解决的问题是什么?

  1. 更好地体现了React的开发思想,即 State => View 的函数式映射
  2. 更好地解决了 Class 组件存在的一些代码冗余、难以逻辑复用的问题

http://www.niftyadmin.cn/n/5443637.html

相关文章

Amazon SageMaker + Stable Diffusion 搭建文本生成图像模型

如果我们的计算机视觉系统要真正理解视觉世界&#xff0c;它们不仅必须能够识别图像&#xff0c;而且必须能够生成图像。文本到图像的 AI 模型仅根据简单的文字输入就可以生成图像。 近两年&#xff0c;以ChatGPT为代表的AIGC技术崭露头角&#xff0c;逐渐从学术研究的象牙塔迈…

Lua中文语言编程源码-第八节,更改loadlib.c 动态库加载器函数, 使Lua加载中文库关键词(加载库,搜索路径,引入)

源码已经更新在CSDN的码库里&#xff1a; git clone https://gitcode.com/funsion/CLua.git 在src文件夹下的loadlib.c 动态库加载器函数&#xff0c;此模块包含适用于具有dlfcn的Unix系统的loadlib实现&#xff0c;适用于Windows系统的实现&#xff0c;以及适用于其他系统的…

蓝桥集训之山峰和山谷

蓝桥集训之山峰和山谷 核心思想&#xff1a;Flood Fill 多加两个bool变量 记录连通块周围是否有比它大/小的数 #include <cstring>#include <iostream>#include <algorithm>#define x first#define y secondusing namespace std;typedef pair<int,int&…

实时数仓之实时数仓架构(Doris)

目前比较流行的实时数仓架构有两类,其中一类是以Flink+Doris为核心的实时数仓架构方案;另一类是以湖仓一体架构为核心的实时数仓架构方案。本文针对Flink+Doris架构进行介绍,这套架构的特点是组件涉及相对较少,架构简单,实时性更高,且易于Lambda架构实现,Doris本身可以支…

C#非强签名dll搜索顺序

由于不是强签名dll&#xff0c;所以无效考虑全局程序集缓存 (GAC)。 预备工作 新建解决方案ClassLibrary1,新建类库ClassLibrary1,新建控制台程序ShowDllLoc。 利用VS添加引用。 一&#xff0c;利用app.config设置codebase&#xff0c;设置dll的加载路径为&#xff1a;code…

电脑照片分辨率怎么调?这款dpi修改工具好用

许多考试平台在上传证件照片的时候&#xff0c;大多都会对图片分辨率有具体要求&#xff0c;但是如果遇上手上的图片分辨率达不到要求&#xff0c;那么怎么改图片分辨率呢&#xff1f;可以利用专业的dpi修改工具来处理&#xff0c;比如今天分享的就是一个在线修改图片分辨率的方…

pandas读写excel,csv

1.读excel 1.to_dict() 函数基本语法 DataFrame.to_dict (self, orientdict , into ) --- 官方文档 函数种只需要填写一个参数&#xff1a;orient 即可 &#xff0c;但对于写入orient的不同&#xff0c;字典的构造方式也不同&#xff0c;官网一共给出了6种&#xff0c…

企业用大模型如何更具「效价比」?百度智能云发布5款大模型新品

服务8万企业用户&#xff0c;累计帮助用户精调1.3万个大模型&#xff0c;帮助用户开发出16万个大模型应用&#xff0c;自2023年12月以来百度智能云千帆大模型平台API日调用量环比增长97%...从一年前国内大模型平台的“开路先锋”到如今的大模型“超级工厂”&#xff0c;百度智能…