简单组件-jsx
class HelloMessage extends React.Component {
render() {
return (
<div>
Hello {this.props.name}
</div>
);
}
}
ReactDOM.render(
<HelloMessage name="Taylor" />,
document.getElementById('hello-example')
);
复制代码
有状态组件-jsx
使用外部数据(通过 this.props 访问)
组件还可以维护其内部的状态数据(通过 this.state 访问)
当组件的状态数据改变时,组件会再次调用 render() 方法重新渲染对应的标记。
注意点: setState 接口一个function为同步, 接受其他类型参数为异步
class Timer extends React.Component {
constructor(props) {
super(props);
this.state = { seconds: 0 };
}
tick() {
this.setState(state => ({
seconds: state.seconds + 1
}));
}
componentDidMount() {
this.interval = setInterval(() => this.tick(), 1000);
}
render() {
return (
<div>
Seconds: {this.state.seconds}
</div>
);
}
}
ReactDOM.render(
<Timer />,
document.getElementById('timer-example')
);
复制代码
函数组件-jsx(状态)
输出的,是可以预知的
function Square(props) {
return (
<button className="square" onClick={props.onClick}>
{props.value}
</button>
);
}
复制代码
汇总的几个区别点: 如下,敬请反驳:
首先:是面向对象和函数式编程这两套不同的设计思想之间的差异。(函数组件更加契合 React 框架的设计理念)
React 组件本身的定位就是函数,一个吃进数据、吐出 UI 的函数。
React 框架的主要工作,就是及时地把声明式的代码转换为命令式的 DOM 操作,把数据层面的描述映射到用户可见的 UI 变化中去。这就意味着从原则上来讲,React 的数据应该总是紧紧地和渲染绑定在一起的,而类组件做不到这一点。
其次:“函数组件会捕获 render 内部的状态,这是两类组件最大的不同”
demo:codesandbox.io/s/pjqnl16lm…
虽然 props 本身是不可变的,但 this 却是可变的,this 上的数据是可以被修改的
Hook 是 React 16.8 的新增特性。它可以让你在不编写 class 的情况下使用 state 以及其他的 React 特性
Hooks 的本质:一套能够使函数组件更强大、更灵活的“钩子”
useState:设置初始化值。
一个问题:如果后面一个state 依赖前一个state的新值。则如何处理?
import React, { useState, useEffect } from "react";
export default function Counter() {
const [count, setState] = useState(1);
const [count2, setState2] = useState(1);
useEffect(() => {});
return (
<>
Count: {count} {count2}
<button onClick={() => setState(1)}>Reset</button>
<button
onClick={() => {
setState(count - 1);
setState2(count);
}}
>
-
</button>
<button
onClick={() => {
setState(count + 1);
setState2(count);
}}
>
+
</button>
</>
);
}
复制代码
useEffect : effect 将在每轮渲染结束后执行,但你可以选择让它 在只有某些值改变的时候 才执行。
基本上90%的情况下,都应该用这个,这个是在render结束后,你的callback函数执行,但是不会block browser painting,算是某种异步的方式吧,但是class的componentDidMount 和componentDidUpdate是同步的,在render结束后就运行,useEffect在大部分场景下都比class的方式性能更好.
useEffect(()=>{
return ()=>{
// 组件卸载时需要清除 effect 创建的诸如订阅或计时器 ID 等资源。要实现这一点,useEffect 函数需返回一个清除函数
}
},[])
复制代码
useEffect(() => {
const subscription = props.source.subscribe();
return () => {
// 清除订阅
subscription.unsubscribe();
};
复制代码
useLayoutEffect:
这个是用在处理DOM的时候,当你的useEffect里面的操作需要处理DOM,并且会改变页面的样式,就需要用这个,否则可能会出现出现闪屏问题, useLayoutEffect里面的callback函数会在DOM更新完成后立即执行,但是会在浏览器进行任何绘制之前运行完成,阻塞了浏览器的绘制.
useRef:获取组件实例对象或者DOM对象,映射,浅拷贝,‘.current’,监听dom某个节点。
forwardref:用于父子组件传值,传递引用,参数是{props,res} -ref,useRef. 子组件接收父组件ref节点
useImperativeHandle: 决定ref组件能接受到的方法和属性
useContext:让你能够读取 context 的值以及订阅 context 的变化,需要在上层组件树中使用 <MyContext.Provider> 来为下层组件提供 context并且在组件外部创建一个公用creteContext,组件之间共享状态
useHook:自定义hook,需要在函数前面加上use关键词
性能优化:
useReducer:类似于redux-reducer,用于处理复杂的state逻辑并且包含多个子值,是usestate的替换方案,使用useReduser还会给触发深更新组件做性能优化浅渲染
import React, { useReducer } from "react";
function init(initialCount) {
return { count: initialCount, count2: initialCount };
}
function reducer(state, action) {
switch (action.type) {
case "increment":
return { count: state.count + 1, count2: state.count + 1 };
case "decrement":
return { count: state.count - 1, count2: state.count + 1 };
case "reset":
return init(action.payload);
default:
throw new Error();
}
}
export default function Counter({ initialCount }) {
const [state, dispatch] = useReducer(reducer, 1, init);
return (
<>
Count: {state.count} {state.count2}
<button
onClick={() => dispatch({ type: "reset", payload: initialCount })}
>
Reset
</button>
<button onClick={() => dispatch({ type: "decrement" })}>-</button>
<button onClick={() => dispatch({ type: "increment" })}>+</button>
</>
);
}
复制代码
useMemo:设置依赖项数组,只有当依赖项数组发生改变才会执行参数函数,解决重复渲染,解决性能优化
useCallback:用来缓存函数的
React 团队面向开发者给出了两条 React-Hooks 的使用原则,原则的内容如下:
只在 React 函数中调用 Hook;
不要在循环、条件或嵌套函数中调用 Hook。
import React, { useState } from "react";
// isMounted 用于记录是否已挂载(是否是首次渲染)
let isMounted = false;
function PersonalInfoComponent() {
// 定义变量的逻辑不变
let name, age, career, setName, setCareer;
// 这里追加对 isMounted 的输出,这是一个 debug 性质的操作
console.log("isMounted is", isMounted);
// 这里追加 if 逻辑:只有在首次渲染(组件还未挂载)时,才获取 name、age 两个状态
if (!isMounted) {
// eslint-disable-next-line
[name, setName] = useState("修言");
// eslint-disable-next-line
[age] = useState("99");
// if 内部的逻辑执行一次后,就将 isMounted 置为 true(说明已挂载,后续都不再是首次渲染了)
isMounted = true;
}
// 对职业信息的获取逻辑不变
[career, setCareer] = useState("我是一个前端,爱吃小熊饼干");
// 这里追加对 career 的输出,这也是一个 debug 性质的操作
console.log("career", career);
// UI 逻辑的改动在于,name 和 age 成了可选的展示项,若值为空,则不展示
return (
<div className="personalInfo">
{name ? <p>姓名:{name}</p> : null}
{age ? <p>年龄:{age}</p> : null}
<p>职业:{career}</p>
<button
onClick={() => {
setName("秀妍");
}}
>
修改姓名
</button>
</div>
);
}
export default PersonalInfoComponent;
复制代码
Hooks 的正常运作,在底层依赖于顺序链表