类组件各个部分的功能
// 方法的箭头函数写法(1)
class Index extends React.Component {
constructor(...arg) {
/* 执行 react 底层 Component 函数 */
super(...arg);
}
state = {}; /* state */
static number = 1; /* 内置静态属性 */
handleClick = () =>
console.log(111); /* 方法: 箭头函数方法直接绑定在this实例上 */
/* 生命周期 */
componentDidMount() {
console.log(Index.number, Index.number1); // 打印 1 , 2
}
/* 渲染函数 */
render() {
return (
// 这里原文是单引号,复制过来md文档的js代码块直接没识别出来
<div onClick={this.handleClick()} style={{ marginTop: "10px" }}>
hello,React!
</div>
);
}
}
Index.number1 = 2; /* 外置静态属性 */
/* 方法: 绑定在 Index 原型链的 方法*/
Index.prototype.handleClick = () => {
console.log(222);
};
// 类组件方法的另一种写法 组件方法(2)
class Index extends React.Component {
constructor(props) {
/* 执行 react 底层 Component 函数 */
super(props);
this.state = {
originValue: 1,
};
// 注意这里的函数需要手动绑定在构造器上
this.handleClick = this.handleClick.bind(this);
}
static number = 1; /* 内置静态属性 */
handleClick() {
console.log(111);
}
/* 生命周期 */
componentDidMount() {
console.log(Index.number, Index.number1); // 打印 1 , 2
}
/* 渲染函数 */
render() {
return (
// 这里原文是单引号,复制过来md文档的js代码块直接没识别出来
<div onClick={this.handleClick()} style={{ marginTop: "10px" }}>
hello,React!
</div>
);
}
}
问:上述绑定了两个 handleClick ,那么点击 div 之后会打印什么呢?
答:结果是 111 。因为在 class 类内部,箭头函数是直接绑定在实例对象上的,而第二个 handleClick 是绑定在 prototype 原型链上的,它们的优先级是:
实例对象上方法属性 > 原型链对象上方法属性。
生命周期详解
https://juejin.cn/post/7046218136177082398?searchId=2023091817010725CC4F0E8FE9A5056D74
类组件事件中的 this 指向问题
- 箭头函数
这种写法,每次 render 调用时都会重新创建一个新的事件处理函数。
class MyComp extends React.Component {
constructor(props) {
super(props);
this.state = {
time: new Date(),
};
}
handleClick() {
console.log(this.state.time);
}
render() {
return <button onClick={() => this.handleClick()}>按钮</button>;
}
}
// this 指向当前组件的实例对象
- 组件方法
render 调用时不会重新创建一个新的事件处理函数,但需要在构造函数中手动绑定 this。
还有一种选择是,我们可以在为元素事件属性赋值的同时绑定 this
class MyComp extends React.Component {
constructor(props) {
super(props);
this.state = {
time: new Date(),
};
this.handleClick = this.handleClick.bind(this);
// 通过 bind 将这个方法绑定到当前组件实例
}
handleClick() {
console.log(this.state.time);
}
render() {
return <button onClick={this.handleClick}>按钮</button>;
}
}
- 属性初始化语法(ES7)
使用官方脚手架 Create React App 创建的项目默认是支持这个特性的,可以在项目中引入 babel 的 transform-class-properties 插件获取这个特性支持。
class MyComp extends React.Component {
constructor(props) {
super(props);
this.state = {
time: new Date(),
};
}
handleClick = () => {
console.log(this.state.time);
}; // 也是箭头函数
render() {
return <button onClick={this.handleClick}>按钮</button>;
}
}
函数组件
不要尝试给函数组件 prototype 绑定属性或方法,即使绑定了也没有任何作用,因为通过上面源码中 React 对函数组件的调用,是采用直接执行函数的方式,而不是通过 new 的方式。
函数组件和类组件本质的区别是什么呢?
对于类组件来说,底层只需要实例化一次,实例中保存了组件的 state 等状态。对于每一次更新只需要调用 render 方法以及对应的生命周期就可以了。但是在函数组件中,每一次更新都是一次新的函数执行,一次函数组件的更新,里面的变量会重新声明。
为了能让函数组件可以保存一些状态,执行一些副作用钩子,React Hooks 应运而生,它可以帮助记录 React 中组件的状态,处理一些额外的副作用。
通信方式
React 一共有 5 种主流的通信方式:
- props 和 callback 方式
这种常用父子间通讯 - ref 方式。
- React-redux 或 React-mobx 状态管理方式。
- context 上下文方式。
- event bus 事件总线。
事件总线使用需要手动绑定与解绑,而且复杂工程不方便维护。由于事件总线使用的机会比较小,这里做个示例
import { BusService } from "./eventBus";
/* event Bus */
function Son() {
const [fatherSay, setFatherSay] = useState("");
React.useEffect(() => {
BusService.on("fatherSay", (value) => {
/* 事件绑定 , 给父组件绑定事件 */
setFatherSay(value);
});
return function () {
BusService.off("fatherSay"); /* 解绑事件 */
};
}, []);
const sonAction = (e) => {
BusService.emit("childSay", e.target.value);
};
return (
<div className="son">
我是子组件
<div> 父组件对我说:{fatherSay} </div>
<input placeholder="我对父组件说" onChange={sonAction} />
</div>
);
}
/* 父组件 */
function Father() {
const [childSay, setChildSay] = useState("");
React.useEffect(() => {
/* 事件绑定 , 给子组件绑定事件 */
BusService.on("childSay", (value) => {
setChildSay(value);
});
return function () {
BusService.off("childSay"); /* 解绑事件 */
};
}, []);
const fatherAction = (e) => BusService.emit("fatherSay", e.target.value);
return (
<div className="box father">
我是父组件
<div> 子组件对我说:{childSay} </div>
<input placeholder="我对子组件说" onChange={fatherAction} />
<Son />
</div>
);
}
类组件的继承
/* 人类 */
class Person extends React.Component {
constructor(props) {
super(props);
console.log("hello , i am person");
}
componentDidMount() {
console.log(1111);
}
eat() {
/* 吃饭 */
}
sleep() {
/* 睡觉 */
}
ddd() {
console.log("打豆豆"); /* 打豆豆 */
}
render() {
return <div>大家好,我是一个person</div>;
}
}
/* 程序员 */
class Programmer extends Person {
constructor(props) {
super(props);
console.log("hello , i am Programmer too");
}
componentDidMount() {
console.log(this);
}
code() {
/* 敲代码 */
}
render() {
return (
<div style={{ marginTop: "50px" }}>
{super.render()} {/* 让 Person 中的 render 执行 */}
我还是一个程序员! {/* 添加自己的内容 */}
</div>
);
}
}
export default Programmer;
HOC 高阶组件
按照 hoc 主要功能,强化 props ,控制渲染 ,赋能组件三个方向对 HOC 编写做了一个详细介绍,和应用场景的介绍,目的让大家在理解高阶组件的时候,更明白什么时候会用到?,怎么样去写?`
里面涵盖的知识点我总一个总结。
属性代理
强化 props & 抽离 state。
条件渲染,控制渲染,分片渲染,懒加载。
劫持事件和生命周期
ref 控制组件实例
添加事件监听器,日志
反向代理
劫持渲染,操纵渲染树
控制/替换生命周期,直接获取组件状态,绑定事件。
作者:我不是外星人
链接:https://juejin.cn/post/6940422320427106335
跨层级捕获 ref
/**
*
* @param {*} Component 原始组件
* @param {*} isRef 是否开启ref模式
*/
function HOC(Component, isRef) {
class Wrap extends React.Component {
render() {
const { forwardedRef, ...otherprops } = this.props;
return <Component ref={forwardedRef} {...otherprops} />;
}
}
if (isRef) {
return React.forwardRef((props, ref) => (
<Wrap forwardedRef={ref} {...props} />
));
}
return Wrap;
}
class Index extends React.Component {
componentDidMount() {
console.log(666);
}
render() {
return <div>hello,world</div>;
}
}
const HocIndex = HOC(Index, true);
export default () => {
const node = useRef(null);
useEffect(() => {
/* 就可以跨层级,捕获到 Index 组件的实例了 */
console.log(node.current.componentDidMount);
}, []);
return (
<div>
<HocIndex ref={node} />
</div>
);
};