React 生命周期图解[1]
我已经把这张图印在脑子里面了,没事就自己画画,中间发散一些自己的思考。u1s1,不知道 react 的生命周期命名为什么要怎么长~~~, 小程序,vue 的都比较短。毕竟使用的频率还是很高的,Hooks 除外。
image.png
constructor 是类通用的构造函数,常用于初始化,算是生命周期的一环。React 后来的版本中类组件也可以不写。
注意:在构造函数中使用时,super
关键字将单独出现,并且必须
在使用 this
关键字之前使用。super 关键字也可以用来调用父对象上的函数。MDN 说明[2]
class JJTest extends React.Component {
// constructor 写法
constructor(props) {
super(props);
this.state = {
count: 0,
};
this.handleClick = this.handleClick.bind(this);
}
// 直接声明
state = {
count: 0,
};
}
触发时机:state 变化、props 变化、forceUpdate
,如上图。
这是一个静态方法, 是一个和组件自身"不相关"的角色. 在这个静态方法中, 除了两个默认的位置参数 nextProps 和 currentState 以外, 你无法访问
任何组件上的数据。
// 初始化/更新时调用
static getDerivedStateFromProps(nextProps, currentState) {
console.log(nextProps, currentState, "getDerivedStateFromProps方法执行");
// 返回值是对currentState进行修改
return {
fatherText: nextProps.text,
};
}
render 函数返回的 JSX 结构,用于描述具体的渲染内容, render 被调用时,它会检查 this.props 和 this.state 的变化并返回以下类型之一:
React 元素
。通常通过 JSX 创建。例如,
会被 React 渲染为 DOM 节点, 会被 React 渲染为自定义组件,无论是
还是 均为 React 元素。
数组或 fragments
。使得 render 方法可以返回多个元素。欲了解更多详细信息,请参阅 fragments 文档。
Portals
。可以渲染子节点到不同的 DOM 子树中。欲了解更多详细信息,请参阅有关 portals 的文档
字符串或数值类型
。它们在 DOM 中会被渲染为文本节点
布尔类型或 null
。什么都不渲染。(主要用于支持返回 test && 的模式,其中 test 为布尔类型。)
注意:如果 shouldComponentUpdate() 返回 false,则不会调用 render()。
Hooks 不需要写 render 函数。要注意的一点是,即使 Hooks 不需要写 render, 没有用到 React.xxx,组件内还是要import React from "react";
的(至于原因,后续深入 Hooks 学一下,大哥们也可以解释下)。React 官方也说了,后续的版本会优化掉这一点。
主要用于组件加载完成时做某些操作,比如发起网络请求或者绑定事件。当做 vue 的 mounted 用就行了,这里需要注意的是:
componentDidMount() 里直接调用 setState()。它将触发额外渲染,也就是两次 render,不过问题不大,主要还是理解。
该方法通过返回 true
或者 false
来确定是否需要触发新的渲染。因为渲染触发最后一道关卡,所以也是性能优化
的必争之地。通过添加判断条件来阻止不必要的渲染。注意:首次渲染
或使用 forceUpdate()
时不会调用该方法。
React 官方提供了一个通用的优化方案,也就是 PureComponent
。PureComponent 的核心原理就是默认实现了 shouldComponentUpdate 函数,在这个函数中对 props 和 state 进行浅比较
,用来判断是否触发更新。
当然 PureComponent 也是有缺点
的,使用的时候一定要注意:由于进行的是浅比较,可能由于深层的数据不一致导致而产生错误的否定判断,从而导致页 面得不到更新
。不适合使用在含有多层嵌套对象
的 state 和 prop 中。
shouldComponentUpdate(nextProps, nextState) {
// 浅比较仅比较值与引用,并不会对 Object 中的每一项值进行比较
if (shadowEqual(nextProps, this.props) || shadowEqual(nextState, this.state) ) {
return true
}
return false
}
在 DOM 更新前
被调用,返回值将作为 componentDidUpdate 的第三个参数。
getSnapshotBeforeUpdate(prevProps, prevState) {
console.log("getSnapshotBeforeUpdate方法执行");
return "componentDidUpdated的第三个参数";
}
首次渲染不会执行此方法。可以使用 setState,会触发重渲染,但一定要小心使用,避免死循环
componentDidUpdate(preProps, preState, valueFromSnapshot) {
console.log("componentDidUpdate方法执行");
console.log("从 getSnapshotBeforeUpdate 获取到的值是", valueFromSnapshot);
}
主要用于一些事件的解绑,资源清理等,比如取消定时器,取消订阅事件
生命周期一定要好好理解,一定要动手写,看一下每种情况下,生命周期的执行结果。上述代码中在React-TypeScript 仓库[3]中都有,可以 clone 下来跑跑看,或者直接访问俊劫学习系统 LifeCycle[4]。还有些其他的生命周期,componentDidCatch, UNSAFE_componentWillMount()
等等,简单了解下就行。
jsx 中一般用 map 来渲染列表循环类的,vue 中直接在 template 中写 v-for 即可
{
list.map((item, index) => {
return <AppCard key={index} title={item.title} onClick={item.onClick} />;
});
}
单独写一个 class 是可以的,动态拼接需要借助 classnames[5] 库
import style from './style.css'
<div className={style.class1 style.class2}</div>
需要注意的:两个括号(样式被当做一个对象来解析),类似-连接的样式属性需要转换成小驼峰写法。
<div style={{ marginTop: 8 }}>样式</div>
u1s1,css 隔离这块还是 vue 的 scoped 好用
create-react-app 中内置了使用 CSS Modules 的配置,和 vue 的 scoped 原理相似,都是在生成的 class 后面加了 hash 值
// style.module.css
.text {
color: blue
}
// app.tsx
import s from "./style.module.css";
class App extends Component {
render() {
return <div className={s.text}>css-module text</div>;
}
}
// 编译后
.text_3FI3s6uz {
color: blue;
}
目前社区里最受欢迎的一款 CSS in JS
方案,个人感觉有点别扭,不太喜欢
//引入styled-components
import styled from "styled-components";
//修改了div的样式
const Title = styled.div`
font-size: 30px;
color: red;
`;
class App extends Component {
render() {
return (
<>
<Title>CSS in JS 方案</Title>
</>
);
}
}
刚开始从 vue 转过来会有些不适应(话说有多少人直接在 vue 里面写 JSX 的),之前用的都是 Vue Sfc 写法,当然多写写就熟悉了。至于 React 采用 JSX 的优劣势,评论区各抒己见哈。
代码对应页面预览[6]
image.png
render() {
return (
<>
<Alert title="控制台展示父子组件生命周期的过程" />
<div className="fatherContainer">
<Button onClick={this.changeText} type="primary">
修改父组件文本内容
</Button>
<Button onClick={this.hideChild} type="danger">
{this.state.hideChild ? "显示" : "隐藏"}子组件
</Button>
{this.state.hideChild ? null : (
<LifeCycle text={this.state.text} count={1} />
)}
</div>
<div>
<BlockLoading loading={this.state.loading} iconSize={64} />
<iframe
src={this.state.lifeCycle}
title="navigation"
width="100%"
height="600px"
onLoad={this.onLoading}
onError={this.onLoading}
></iframe>
</div>
</>
);
}
组件这块,个人感觉和 vue 差别还是比较大的,颗粒度更细致,当然也增加了一定难度。这里就简单例举一个TS版本的,带 Icon 的标题组件
import cn from "classnames";
import React from "react";
import "./style/index.less";
import { Icon,IIconProps } from "zent";
interface IProps {
title: string;
iconType?: IIconProps['type'];
isShowIcon?: boolean;
iconClassName?: string;
titleClassName?: string;
}
export const ContentTitle: React.FC<IProps> = (props) => {
const { title, iconType = 'youzan', isShowIcon = false , iconClassName, titleClassName, ...popProps } = props;
return (
<div className={cn("content-title", titleClassName)}>
{title}
{isShowIcon && <Icon
className={cn("content-title__icon", iconClassName)}
{...popProps}
type={iconType}
/>}
</div>
);
};
export default ContentTitle;
和 vue mixins
相同,都是为了解决代码复用
的问题,但 react 中已经废弃 mixins,vue 中也不推荐使用。主要是会带来命名冲突,相互依赖,不方便维护
等一些缺点。
高阶组件其实就是处理 react 组件的函数
,简单理解就是和 ES6 中提供的 export/import 作用相似,不同点在于:高阶组件会进行加工后
再导出你需要的东西。类似于方程式:y = ax + b
, x 是入口(组件),会根据 a 和 b 进行计算,得到最终的 y(处理后的组件) 给到你用。
官网的实现 Demo: 高阶组件[7]
一个简单的高阶组件(实现有两种方式:属性代理和反向继承):
// 属性代理: 组件属性的一些修改
const JJHOC = (WrappedComponent) => {
return class NewComponent extends React.Component {
render() {
const newProps = { type: "HOC" };
return <WrappedComponent {...this.props} {...newProps} />;
}
};
};
// 反向继承: 在render() 方法中返回 super.render() 方法
const JJHOC = (WrappedComponent) => {
return class NewComponent extends WrappedComponent {
render() {
return super.render();
}
};
};
和 vue 不同的是,react props 传值可以直接写
,不需要声明。在 props 上挂载 function
,就相当于是 vue 的$emit
。同样需要注意的是子组件不可以修改 props
的值
import React from "react";
function Child(props) {
const sendMsg = (msg) => {
props.onClick("子组件的消息");
};
return (
<div>
<div>子组件标题:{props.title}</div>
<button onClick={() => sendMsg("子组件消息")}> 子传父 </button>
</div>
);
}
function Father() {
const onClick = (msg) => {
console.log(`父组件接收:${msg}`);
};
return (
<div>
<Child title="组件props传值测试" onClick={onClick}></Child>
</div>
);
}
export default Father;
React Context 官网说明[8],跨组件传值。创建了一个上下文,同 context 内的组件都可以 通过 Provider 配合 value 使用数据
import * as React from "react";
import { Button } from "zent";
// Context 可以让我们无须明确地传遍每一个组件,就能将值深入传递进组件树。
// 为当前的 theme 创建一个 context(“primary”为默认值)。
const ThemeContext = React.createContext("primary");
export default class App extends React.Component {
render() {
// 使用一个 Provider 来将当前的 theme 传递给以下的组件树。
// 无论多深,任何组件都能读取这个值。
// 在这个例子中,我们将 danger 作为当前的值传递下去。
return (
<ThemeContext.Provider value="danger">
<Toolbar />
</ThemeContext.Provider>
);
}
}
// 中间的组件再也不必指明往下传递 theme 了。
function Toolbar() {
return (
<div>
<ThemedButton />
</div>
);
}
class ThemedButton extends React.Component {
// 指定 contextType 读取当前的 theme context。
// React 会往上找到最近的 theme Provider,然后使用它的值。
// 在这个例子中,当前的 theme 值为 “danger”。
static contextType = ThemeContext;
render() {
return <Button type={this.context}>context测试</Button>;
}
}
Redux 中文文档[9]
redux 的三大核心:
不一定非要用,很多项目 context 就已经够用了
使用 createStore
创建一个 store 并通过 Provider
把它放到容器组件中
// index.js
const store = createStore(appReducer);
ReactDOM.render(
<Provider store={store}>
<App />
</Provider>,
document.getElementById("root");
);
和 vuex 相似,都是通过 action
来修改数据
// action.js
export const addConst = (payload) => {
type: "ADD_CONST",
}
export const minusConst = (payload) => {
type: "MINUS_CONST",
}
当 dispatch 触发相应的方法,执行对应的操作,修改 store 数据。
// appReducer.js
const initialState = { count: 0 };
const reducer = (state = initialState, action) => {
switch (action.type) {
case "ADD_CONST":
return { count: count + 1 };
case "MINUS_CONST":
return { count: count - 1 };
default:
return state;
}
};
export default reducer;
import React from "react";
import { connect } from "react-redux";
const ReduxDemo: React.FC = (props) => {
const addCount = () => {
const { dispatch } = props;
dispatch({
type: "ADD_CONST",
});
};
const minusCount = () => {
const { dispatch } = props;
dispatch({
type: "MINUS_CONST",
});
};
return (
<div>
<button onClick={addCount}>加</button>
<button onClick={minusCount}>减</button>
<div>{props.state}</div>
</div>
);
};
const mapStateToProps = (state) => {
return {
count: state.count,
};
};
export default connect(mapStateToProps)(ReduxDemo);
React 官网 使用 PropTypes 进行类型检查[10] react props 不是必须声明的,但是如果项目规范起来,就需要在 propTypes 中声明 props 类型,注意需要引入prop-types
库
不过现在更多的是通过 typescript
来校验类型了,开发阶段就能发现问题。
import * as React from "react";
import PropTypes from "prop-types";
interface IProps {
name: string;
}
const PropsDemo: React.FC<IProps> = ({ name }) => {
return <h1>Hello, {name}</h1>;
};
PropsDemo.propTypes = {
name: PropTypes.string,
};
import React, { Component } from "react";
import Admin from "./pages/admin/admin";
import Login from "./pages/login/Login";
import { HashRouter, Route, Switch } from "react-router-dom";
class App extends Component {
render() {
return (
<HashRouter>
<Switch>
<Route path="/" component={Admin}></Route>
<Route path="/login" component={Login}></Route>
</Switch>
</HashRouter>
);
}
}
export default App;
// router
<Route path='/path/:id' component={Path}/>
// 传参
<link to="/path/789">xxx</Link>
this.props.history.push({pathname:`/path/${id}`});
// 获取
this.props.match.params.id
// router
<Route path='/query' component={Query}/>
// 传参
<Link to={{ path : '/query' , query : { id : '789' }}}>xxx</Link>
this.props.history.push({pathname:"/query",query: { id : '789' }});
// 获取
this.props.location.query.id
// 跳转
let history = useHistory();
history.push("/");
// 获取
useLocation();
useParams();
useRouteMatch();
exact 是 Route 下的一条属性,一般而言,react 路由会匹配所有匹配到的路由组价,exact 能够使得路由的匹配更严格一些。
exact 的值为 bool 型,为 true 是表示严格匹配,为 false 时为正常匹配。
如在 exact 为 true 时,’/link’与’/’是不匹配的,但是在 false 的情况下它们又是匹配的。<Route path="/home" component={Home} exact></Route>
学完生命周期,多练习 JSX,配合 React Router 和 Redux 多写写组件,基本就能上手开发了。没有过多的 API 需要学习,写起来也比较自由。React 虽然生态强大,选着性比较多,但是这样产生了一个问题:什么是 React 的最佳实践?
参考资料
[1]React 生命周期图解: https://projects.wojtekmaj.pl/react-lifecycle-methods-diagram/
[2]MDN 说明: https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Operators/super#%E6%8F%8F%E8%BF%B0
[3]React-TypeScript 仓库: https://github.com/alexwjj/React-TypeScript
[4]俊劫学习系统 LifeCycle: https://alexwjj.github.io/study/#/demo
[5]classnames: https://github.com/JedWatson/classnames
[6]代码对应页面预览: https://alexwjj.github.io/study/#/demo
[7]高阶组件: https://zh-hans.reactjs.org/docs/higher-order-components.html
[8]React Context 官网说明: https://zh-hans.reactjs.org/docs/context.html
[9]Redux 中文文档: http://cn.redux.js.org/
[10]React 官网 使用 PropTypes 进行类型检查: https://zh-hans.reactjs.org/docs/typechecking-with-proptypes.html
[11]React Router 官网: https://reactrouter.com/web/guides/quick-start
[12]React Router 中文文档: http://react-guide.github.io/react-router-cn/
本文由哈喽比特于3年以前收录,如有侵权请联系我们。
文章来源:https://mp.weixin.qq.com/s/JtGMCUfm7v4ibKRg8pByEw
京东创始人刘强东和其妻子章泽天最近成为了互联网舆论关注的焦点。有关他们“移民美国”和在美国购买豪宅的传言在互联网上广泛传播。然而,京东官方通过微博发言人发布的消息澄清了这些传言,称这些言论纯属虚假信息和蓄意捏造。
日前,据博主“@超能数码君老周”爆料,国内三大运营商中国移动、中国电信和中国联通预计将集体采购百万台规模的华为Mate60系列手机。
据报道,荷兰半导体设备公司ASML正看到美国对华遏制政策的负面影响。阿斯麦(ASML)CEO彼得·温宁克在一档电视节目中分享了他对中国大陆问题以及该公司面临的出口管制和保护主义的看法。彼得曾在多个场合表达了他对出口管制以及中荷经济关系的担忧。
今年早些时候,抖音悄然上线了一款名为“青桃”的 App,Slogan 为“看见你的热爱”,根据应用介绍可知,“青桃”是一个属于年轻人的兴趣知识视频平台,由抖音官方出品的中长视频关联版本,整体风格有些类似B站。
日前,威马汽车首席数据官梅松林转发了一份“世界各国地区拥车率排行榜”,同时,他发文表示:中国汽车普及率低于非洲国家尼日利亚,每百户家庭仅17户有车。意大利世界排名第一,每十户中九户有车。
近日,一项新的研究发现,维生素 C 和 E 等抗氧化剂会激活一种机制,刺激癌症肿瘤中新血管的生长,帮助它们生长和扩散。
据媒体援引消息人士报道,苹果公司正在测试使用3D打印技术来生产其智能手表的钢质底盘。消息传出后,3D系统一度大涨超10%,不过截至周三收盘,该股涨幅回落至2%以内。
9月2日,坐拥千万粉丝的网红主播“秀才”账号被封禁,在社交媒体平台上引发热议。平台相关负责人表示,“秀才”账号违反平台相关规定,已封禁。据知情人士透露,秀才近期被举报存在违法行为,这可能是他被封禁的部分原因。据悉,“秀才”年龄39岁,是安徽省亳州市蒙城县人,抖音网红,粉丝数量超1200万。他曾被称为“中老年...
9月3日消息,亚马逊的一些股东,包括持有该公司股票的一家养老基金,日前对亚马逊、其创始人贝索斯和其董事会提起诉讼,指控他们在为 Project Kuiper 卫星星座项目购买发射服务时“违反了信义义务”。
据消息,为推广自家应用,苹果现推出了一个名为“Apps by Apple”的网站,展示了苹果为旗下产品(如 iPhone、iPad、Apple Watch、Mac 和 Apple TV)开发的各种应用程序。
特斯拉本周在美国大幅下调Model S和X售价,引发了该公司一些最坚定支持者的不满。知名特斯拉多头、未来基金(Future Fund)管理合伙人加里·布莱克发帖称,降价是一种“短期麻醉剂”,会让潜在客户等待进一步降价。
据外媒9月2日报道,荷兰半导体设备制造商阿斯麦称,尽管荷兰政府颁布的半导体设备出口管制新规9月正式生效,但该公司已获得在2023年底以前向中国运送受限制芯片制造机器的许可。
近日,根据美国证券交易委员会的文件显示,苹果卫星服务提供商 Globalstar 近期向马斯克旗下的 SpaceX 支付 6400 万美元(约 4.65 亿元人民币)。用于在 2023-2025 年期间,发射卫星,进一步扩展苹果 iPhone 系列的 SOS 卫星服务。
据报道,马斯克旗下社交平台𝕏(推特)日前调整了隐私政策,允许 𝕏 使用用户发布的信息来训练其人工智能(AI)模型。新的隐私政策将于 9 月 29 日生效。新政策规定,𝕏可能会使用所收集到的平台信息和公开可用的信息,来帮助训练 𝕏 的机器学习或人工智能模型。
9月2日,荣耀CEO赵明在采访中谈及华为手机回归时表示,替老同事们高兴,觉得手机行业,由于华为的回归,让竞争充满了更多的可能性和更多的魅力,对行业来说也是件好事。
《自然》30日发表的一篇论文报道了一个名为Swift的人工智能(AI)系统,该系统驾驶无人机的能力可在真实世界中一对一冠军赛里战胜人类对手。
近日,非营利组织纽约真菌学会(NYMS)发出警告,表示亚马逊为代表的电商平台上,充斥着各种AI生成的蘑菇觅食科普书籍,其中存在诸多错误。
社交媒体平台𝕏(原推特)新隐私政策提到:“在您同意的情况下,我们可能出于安全、安保和身份识别目的收集和使用您的生物识别信息。”
2023年德国柏林消费电子展上,各大企业都带来了最新的理念和产品,而高端化、本土化的中国产品正在不断吸引欧洲等国际市场的目光。
罗永浩日前在直播中吐槽苹果即将推出的 iPhone 新品,具体内容为:“以我对我‘子公司’的了解,我认为 iPhone 15 跟 iPhone 14 不会有什么区别的,除了序(列)号变了,这个‘不要脸’的东西,这个‘臭厨子’。