【React】《React学习手册》读书笔记

React运行机制

React主要包含两个库React和ReactDom。

React元素

React通过JSX让我们可以不操作DOM元素,通过声明式用js对象来描述我们想让React构建的元素。我们写的JSX标签,会在编译阶段变成React.createElement()来创建React元素。
React.createElement("h1",{id:"recipe-0"},"innertext...")

  • 创建元素类型
  • 属性
  • 子元素
    一个React元素结构如下:
    在这里插入图片描述
  • type属性:告知React要创建的元素类型
  • props属性:表示构建一个DOM元素需要的数据和子元素
    • children属性:表示嵌套显示在文本里

ReactDOM

创建React元素后,我们还需要渲染。使用ReactDOM.render(element,document.getElementById()"root") 在DOM中渲染

为什么将渲染和创建React元素分开:

  • 性能优化:减少频繁操作DOM元素的开销
  • 方便渲染到不同的设备上,例如浏览器DOM,canvas或者原生App上。
  • 便于SSR渲染

子元素

props.children 为标签的子元素。

其他React元素也可成为子元素。

JSX

React.createElement()来创建React元素显然太复杂了,我们需要使用JSX。

JSX是JS与XML的结合,是对JavaScript的扩展,使用基于标签的句法直接定义React元素。
在这里插入图片描述

Babel

JSX可读性高,但浏览器无法解释。所以需要Babel把JSX都转换成React.createElement() 调用。

Babel负责编译:如果我们使用了JSX或者新的JS特性,浏览器都无法解释,让Babel把我们写的js转换成浏览器可以解释的版本。

JavaScript 引擎只能实现标准中的一部分,所以当我们需要使用新特性时可以使用:

  • 转译器:例如Babel,重写旧的语法结构
  • 垫片 Polyfills:更新/添加新的内建函数,没有语法更改所以转译器会失效。

React状态管理

如果不借助其他工具,想要管理组件树中的状态,可以在组件树根部存储状态。防止状态数据分散在多个组件中不便于追踪和修改。

  • 沿着组件树向下发送状态。尽量写纯组件(即不含状态的函数组件),通过属性获取状态。
  • 沿组件树向上发送交互:向下传递操作函数,将属性传递给亲组件

使用Ref

使用useRef,在JSX标签上添加ref属性,就可直接获取到DOM元素进行操作。

自定义钩子

对于重复的操作可以提取出来定义为函数。

更多钩子

useEffect

对于副作用,如果直接写入函数组件会阻塞渲染。副作用就是函数组件除了返回UI以外所做的其他事情。

useEffect(()⇒fn(),[])

通过依赖数组控制执行副作用的时机。

  • 监听数组内变量,发生变化就调用
  • 空数组:首次渲染后调用
  • useEffect返回的函数将在把组件从组件树上移除时调用,做清理工作

渲染→useEffect

useMemo、useCallback

对于非原始值的数据作为依赖,每次渲染不管是否变化都是新的值。可以在函数组件外定义。对于必须在组件作用域内定义的值,每次渲染都都会重新创建实例,如果useEffect要依赖该值,那么不管该值有没有变化,每次渲染都会执行。

这个时候需要用useMemo定义。useMemo(()⇒fn(),[]) 调用一个函数,保存得到的结果;如果使用相同的输入调用函数,返回缓存的值。

对于函数,使用useCallback备忘。

useLayoutEffect

执行时间:渲染→调用useLayoutEffect→浏览器绘制,即把组件元素加入DOM中→调用useEffect

使用useLayoutEffect对浏览器绘制操作。

钩子使用规则

  • 钩子只在组件的作用域外运行

  • 钩子只在顶层代码中调用

    调用钩子后,React会在数组中保存钩子的值,所以不能在条件语句中使用钩子,否则索引会变。

  • 在钩子内部进行条件判断,异步操作

useReducer

const [ ①state , ②dispatch ] = useReducer(③reducer,initState)

reducer是一个默认接收当前状态的纯函数,还可接收通过dispatch传递的参数操作当前状态,返回一个新状态。dispatch用于调用reducer更新状态。

当对数据更新需要复杂的操作时或者对复杂的状态进行更新时,可以使用useReducer代替useState。

提升组件性能

提升组件性能:

  • 避免不必要的渲染
  • 减少渲染传播的时间:避免非必要渲染:memo、useMemo、useCallback

对于纯组件,可以使用memo()包装组件,创建只在属性有变化时渲染的组件。

import React, { useState, memo } from "react";
const Cat = ({ name }) => {
console.log(`rendering ${name}`);
return <p>{name}</p>;
};
const PureCat = memo(Cat);

但如果增加了函数属性,memo就会失效,需要定义更具体的规则指明何时重新渲染组件:

const RenderCatOnce = memo(Cat, () => true);
const AlwaysRenderCat = memo(Cat, () => false);

传给memo的第二个参数是一个断言(只返回布尔值)。返回false则重新渲染,true则不重新渲染。

也可通过useCallback备忘函数属性,useMemo备忘对象属性。

处理数据

发起http请求

发起http请求需要处理三种状态:data、error、loading。修改data,渲染出错error详情以及在请求处理待定状态时,显示”loading“信息。

function GitHubUser({ login }) {
const [data, setData] = useState();
const [error, setError] = useState();
const [loading, setLoading] = useState(false);
useEffect(() => {
if (!login) return;
setLoading(true);
fetch(`https://api.github.com/users/${login}`)
.then(data => data.json())
.then(setData)
.then(() => setLoading(false))
.catch(setError);
}, [login]);
if (loading) return <h1>loading...</h1>;
if (error)
return <pre>{JSON.stringify(error, null, 2)}</pre>;
if (!data) return null;
return (
<div className="githubUser">
<img
src={data.avatar_url}
alt={data.login}
style={{ width: 200 }}
/>
<div>
<h1>{data.login}</h1>
{data.name && <p>{data.name}</p>}
{data.location && <p>{data.location}</p>}
</div>
</div>
);
}

自定义useFetch钩子

export function useFetch(uri) {
const [data, setData] = useState();
const [error, setError] = useState();
const [loading, setLoading] = useState(true);
useEffect(() => {
if (!uri) return;
fetch(uri)
.then(data => data.json())
.then(setData)
.then(() => setLoading(false))
.catch(setError);
}, [uri]);
return {
loading,
data,
error
};
}

创建Fetch组件

由于渲染部分的逻辑是一样的,可以将渲染的部分也作为参数创建一个可以复用的组件。

function Fetch({
uri,
renderSuccess,
loadingFallback = <p>loading...</p>,
renderError = error => (
<pre>{JSON.stringify(error, null, 2)}</pre>
)
}) {
const { loading, data, error } = useFetch(uri);
if (loading) return loadingFallback;
if (error) return renderError(error);
if (data) return renderSuccess({ data });
}

渲染属性

  • 作为属性传入的组件,在满足条件时被渲染
  • 返回组件的函数属性

可以提高组件的可重用性,把复杂机制和单调的样板代码抽离出来。

function List({ data = [], renderItem, renderEmpty }) {
return !data.length ? (
renderEmpty
) : (
<ul>
{data.map((item, i) => (
<li key={i}>{renderItem(item)}</li>
))}
</ul>
);
}

传入如何渲染的函数和data为empty时代替渲染的组件。这个List组件就可重用。

虚拟化

对于大型数据,我们不应该全部渲染,应该只在窗口和窗口上下渲染部分数据。随着用户滚动屏幕,消除用户已经看到的结果。

不用造轮子,可以使用虚拟化列表组件react-window等实现。

import React from "react";
import { FixedSizeList } from "react-window";
import faker from "faker";
const bigList = [...Array(5000)].map(() => ({
name: faker.name.findName(),
email: faker.internet.email(),
avatar: faker.internet.avatar()
}));
export default function App() {
const renderRow = ({ index, style }) => (
<div style={{ ...style, ...{ display: "flex" } }}>
<img
src={bigList[index].avatar}
alt={bigList[index].name}
width={50}
/>
<p>
{bigList[index].name} - {bigList[index].email}
</p>
</div>
);
return (
<FixedSizeList
height={window.innerHeight}
width={window.innerWidth - 20}
itemCount={bigList.length}
itemSize={50}
>
{renderRow}
</FixedSizeList>
);
}
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值