React system study notes -- super basic -- super detailed -- super concise -- React component-oriented programming (2)

Part Ⅱ: React component-oriented programming

1 Basic understanding and use

Debugging with React DevTools

React Developer Tools

important point

  • Component names must be capitalized

  • A virtual DOM element can only have one root element

  • Virtual DOM elements must have closing tags < />

The basic process of rendering class component tags

  • React internally creates component instance objects

  • Call render() to get virtual DOM and parse it into real DOM

  • Insert inside the specified page element

1.1 Define components in React

Functionally Declare Components

<script type="text/babel">
	//1.创建函数式组件
	function MyComponent(){
    
    
		console.log(this); //此处的this是undefined,因为babel编译后开启了严格模式
		return <h2>我是用函数定义的组件(适用于【简单组件】的定义)</h2>
	}
	//2.渲染组件到页面
	ReactDOM.render(<MyComponent/>,document.getElementById('test'))
</script>

After executing ReactDOM.render(<MyComponent/>the ..., what happened

  • React parses the component tag and finds the MyComponent component

  • It is found that the component is defined using a function, and then the function is called to convert the returned virtual DOM into a real DOM, which is then rendered on the page

Class components (the following examples refer to class components)

<script type="text/babel">
	//1.创建类式组件
	//必须继承Component类,必须要写render,必须有返回值
	class MyComponent extends React.Component {
    
    
		render(){
    
    
			//render是放在哪里的?—— MyComponent的原型对象上,供实例使用。
			//render中的this是谁?—— MyComponent的实例对象 <=> MyComponent组件实例对象。
			console.log('render中的this:',this);
			return <h2>我是用类定义的组件(适用于【复杂组件】的定义)</h2>
		}
	}
	//2.渲染组件到页面
	ReactDOM.render(<MyComponent/>,document.getElementById('test'))
</script>

After executing ReactDOM.render(<MyComponent/>the ..., what happened

  • React parses the component tag and finds the MyComponent component

  • It is found that the component is defined using a class, and then an instance of the class is newly created, and the render method on the prototype is called through the instance

  • Convert the virtual DOM returned by render to real DOM, and then render it on the page

Simple and complex components

Stateful components are complex components, stateless components are simple components

State-driven page: data is placed in the state of the component, and the change of data will drive the display of the page

The state state is on the component instance

2 One of the three major attributes of component instances: state

2.1 Understanding of state

  • state is the most important attribute of the component object, and the value is an object (can contain multiple key:value combinations)

  • The component is called 状态机, and the corresponding page display is updated by updating the state of the component (re-rendering the component)

  • The data is placed in the state, and the state drives the page display! State-driven page display! State-driven page display!

2.2 Points to note

The this in the render method in the component is the component instance object

In the component custom method, this is undefined (used as a callback), how to solve it?

  • Mandatory binding of this: via bind() of the function object

  • this.changeWeather = this.changeWeather.bind(this)In this way, after writing, the method on the instance object itself is called instead of hanging on the prototype

  • assignment statement + arrow function推荐

Status data, which cannot be directly modified or updated, needs to be setStateupdated with

The methods in the class are basically used as event callbacks, so figure out this

If you want to use it, you need to write many methods in the constructor this.changeWeather = this.changeWeather.bind(this), which is too cumbersome (simplify the constructor, keep or call this----simplified writing)

2.3 Code example

Normally use the bind() of the function object

<script type="text/babel">
	//1.创建组件
	class Weather extends React.Component {
    
    

		//构造器调用几次? ———— 1次
		constructor(props) {
    
    
			console.log('constructor');
			super(props)
			//初始化状态
			this.state = {
    
     isHot: false, wind: '微风' }
			//解决changeWeather中this指向问题---原型上得有,然后生成这个实例身上自身的方法
			this.changeWeather = this.changeWeather.bind(this)
		}

		//render调用几次? ———— 1+n次 1是初始化的那次 n是状态更新的次数
		render() {
    
    
			console.log('render');
			//读取状态
			const {
    
     isHot, wind } = this.state
			//绑定事件不用写括号,不然就是把返回值交给onClick,我们是要把整个回调函数交给onClick
			//这边现在调用的changeWeather方法是实例自身上的
			return <h1 onClick={
    
    this.changeWeather}>今天天气很{
    
    isHot ? '炎热' : '凉爽'}{
    
    wind}</h1>
		}

		//changeWeather调用几次? ———— 点几次调几次
		changeWeather() {
    
    
			//changeWeather放在哪里? ———— Weather的原型对象上,供实例使用
			//由于changeWeather是作为onClick的回调,所以不是通过实例调用的,是直接调用this应该是window
			//但类中的方法默认开启了局部的严格模式,所以changeWeather中的this为undefined

			console.log('changeWeather');
			//获取原来的isHot值
			const isHot = this.state.isHot
			//严重注意:状态必须通过setState进行更新,且更新是一种合并,不是替换。
			this.setState({
    
     isHot: !isHot })
			console.log(this);

			//严重注意:状态(state)不可直接更改,下面这行就是直接更改!!!
			//this.state.isHot = !isHot //这是错误的写法
		}
	}
	//2.渲染组件到页面
	ReactDOM.render(<Weather />, document.getElementById('test'))
</script>

Shorthand method: the form of assignment statement + arrow function , important! important! important!

The assignment statement written in the class is placed on its own instance, and then the method is written as an arrow function. The this of the arrow function is the instance object, so the constructor can be simplified

(The purpose of writing the constructor is to customize some initialization attributes. Now write the attributes as assignment statements and put them in the class, and you will have state directly)

//1.创建组件
class Weather extends React.Component{
    
    
	//初始化状态
	state = {
    
    isHot:false,wind:'微风'}
	render(){
    
    
		const {
    
    isHot,wind} = this.state
		return <h1 onClick={
    
    this.changeWeather}>今天天气很{
    
    isHot ? '炎热' : '凉爽'}{
    
    wind}</h1>
	}
	//自定义方法————要用赋值语句的形式+箭头函数
	changeWeather = ()=>{
    
    
		const isHot = this.state.isHot
		this.setState({
    
    isHot:!isHot})
	}
}
//2.渲染组件到页面
ReactDOM.render(<Weather/>,document.getElementById('test'))

3 One of the three major attributes of component instances: props

3.1 understanding of props

  • Each component object will have props (short for properties) attribute

  • All properties of component tags are stored in props

3.2 The role and attention of props

  • Transfer changing data from outside the component to inside the component through the label attribute

  • Note: Do not modify props data inside the component, props are read-only

  • If the incoming data is too long, the retrieved object data can then be written as {...p}incoming

  • You can restrict the incoming data type, if you don't pass it, you can also specify the default value

  • Add attributes to the class itself, not to the instance of the class, use static static

3.3 Code example

Class components use props, the most basic way to use

//创建组件
class Person extends React.Component{
    
    
	render(){
    
    
		// console.log(this);
		const {
    
    name,age,sex} = this.props
		return (
			<ul>
				<li>姓名:{
    
    name}</li>
				<li>性别:{
    
    sex}</li>
				<li>年龄:{
    
    age+1}</li>
			</ul>
		)
	}
}
//渲染组件到页面
ReactDOM.render(<Person name="jerry" age={
    
    19}  sex="男"/>,document.getElementById('test1'))
ReactDOM.render(<Person name="tom" age={
    
    18} sex="女"/>,document.getElementById('test2'))
//上面这样写有一些问题:信息很多要写太长,能不能批量操作
const p = {
    
    name:'老刘',age:18,sex:'女'}
// console.log('@',...p);
// ReactDOM.render(<Person name={p.name} age={p.age} sex={p.sex}/>,document.getElementById('test3'))
//此处使用赋值解构方式,使得代码更简洁,上面的语法糖,关键在于捞回来的数据要一样才能这样写
ReactDOM.render(<Person {
    
    ...p}/>,document.getElementById('test3'))
//...p在原生js本身是错误的,但是{...p}可以,相当于复制了一个对象。在react中的{}作为分隔符使用,但是我们引入了react加上babel就可以直接分解对象,也就是可以直接写...p,但是注意仅可以作为标签的属性使用,不是随便可以使用的
//对传入数据的类型进行限制,此处限制可以换成typrScript
<script type="text/javascript" src="../js/prop-types.js"></script>
Person.propTypes = {
    
    
	name:PropTypes.string.isRequired, //限制name必传,且为字符串---16版本之后不在React上面取了,引入prop-types依赖包
	sex:PropTypes.string,//限制sex为字符串
	age:PropTypes.number,//限制age为数值
	speak:PropTypes.func,//限制speak为函数
}

//不传的话,指定默认标签属性值
Person.defaultProps = {
    
    
	sex:'男',//sex默认值为男
	age:18 //age默认值为18
}
//props简写,把类型限制和默认值写在类里面
static propTypes = {
    
    
	name:PropTypes.string.isRequired, //限制name必传,且为字符串
	sex:PropTypes.string,//限制sex为字符串
	age:PropTypes.number,//限制age为数值
}

static defaultProps = {
    
    
	sex:'男',//sex默认值为男
	age:18 //age默认值为18
}

Function components use props because functions can receive parameters

//创建组件
function Person (props){
    
    
	const {
    
    name,age,sex} = props
	return (
			<ul>
				<li>姓名:{
    
    name}</li>
				<li>性别:{
    
    sex}</li>
				<li>年龄:{
    
    age}</li>
			</ul>
		)
}

//渲染组件到页面
ReactDOM.render(<Person name="jerry"/>,document.getElementById('test1'))
//函数的类型限制要写在外面

4 One of the three major attributes of component instances: refs

4.1 Understanding of refs

Tags in components can define refs to identify themselves, similar to native ids, refs collect real dom nodes

4.2 Code example

There is a problem with ref( ) in string form 不推荐,将被淘汰: it is not efficient

//展示左侧输入框的数据
	showData = ()=>{
    
    
		const {
    
    input1} = this.refs
		alert(input1.value)
	}
	//展示右侧输入框的数据
	showData2 = ()=>{
    
    
		const {
    
    input2} = this.refs
		alert(input2.value)
	}
	render(){
    
    
		return(
			<div>
				<input ref="input1" type="text" placeholder="点击按钮提示数据"/>&nbsp;
				<button onClick={
    
    this.showData}>点我提示左侧的数据</button>&nbsp;
				<input ref="input2" onBlur={
    
    this.showData2} type="text" placeholder="失去焦点提示数据"/>
			</div>
		)
	}
}

The ref in the callback form stores the real DOM node in the component instance this, and the callback is directly written as an introverted function, which has no effect

/**下面的this指的是组件实例,我直接this.input1 = c 意思是给实例上的input1赋值,之后直接通过调用打印得到*/
//回调函数的内联写法,会有问题,就是更新数据会重新调用两次函数,第一次传入null,第二次才是传入dom节点,这个是每次更新生成新的函数
//展示左侧输入框的数据
	showData = ()=>{
    
    
		const {
    
    input1} = this//在实例身上取
		alert(input1.value)
	}
	//展示右侧输入框的数据
	showData2 = ()=>{
    
    
		const {
    
    input2} = this
		alert(input2.value)
	}
	render(){
    
    
		return(
			<div>
				<input ref={
    
    c => this.input1 = c } type="text" placeholder="点击按钮提示数据"/>&nbsp;
				<button onClick={
    
    this.showData}>点我提示左侧的数据</button>&nbsp;
				<input onBlur={
    
    this.showData2} ref={
    
    c => this.input2 = c } type="text" placeholder="失去焦点提示数据"/>&nbsp;
			</div>
		)
	}
}
//回调函数写成class的绑定函数,就不会频繁调用,不会生成新的函数
saveInput = (c)=>{
    
    
	this.input1 = c;
	console.log('@',c);
}

render(){
    
    
	return(
		<div>
			<input ref={
    
    this.saveInput} type="text"/><br/><br/>
			<button onClick={
    
    this.showData}>点我提示左侧的数据</button>&nbsp;
			<input onBlur={
    
    this.showData2} ref={
    
    c => this.input2 = c } type="text" placeholder="失去焦点提示数据"/>&nbsp;
		</div>
	)
}

createRef creates a ref container最推荐的

/*React.createRef调用后可以返回一个容器,该容器可以存储被ref所标识的节点,该容器是“专人专用”的*/
//用几个就要创建几个容器,一个容器只能放一个ref标识的节点
myRef = React.createRef()
myRef2 = React.createRef()
//展示左侧输入框的数据
showData = ()=>{
    
    
    // 取出保存在容器里的节点
	alert(this.myRef.current.value);
}
//展示右侧输入框的数据
showData2 = ()=>{
    
    
	alert(this.myRef2.current.value);
}
render(){
    
    
	return(
		<div>
             {
    
    /*把ref所在的节点存储到刚才实例身上生成的容器myRef里面*/}
			<input ref={
    
    this.myRef} type="text" placeholder="点击按钮提示数据"/>&nbsp;
			<button onClick={
    
    this.showData}>点我提示左侧的数据</button>&nbsp;
			<input onBlur={
    
    this.showData2} ref={
    
    this.myRef2} type="text" placeholder="失去焦点提示数据"/>&nbsp;
		</div>
	)
}

5 Event handling and collecting form data

5.1 Event Handling

Specify the event handler function by onXxxattribute (note case) (native: onclick, React: onClick)

  • React uses custom (synthetic events, not native DOM events) - for better compatibility

  • Events in React are handled through event delegation (delegating to the outermost element of the component) - for more efficiency

Get the DOM element object where the event occurred through event.target ----- don't overuse ref

//不过度使用ref:发生事件的元素正好是我要操作的元素时,就可以避免使用ref(受控组件可以发生事件与操作的元素不同)
//失去焦点的时候react会帮你调用这个函数,并把事件对象event传进去
//就可以拿到发生事件的事件源event.target,event.target就是input框
showData2 = (event) => {
    
    
	alert(event.target.value);
}

render() {
    
    
	return (
		<div>
			<input ref={
    
    this.myRef} type="text" placeholder="点击按钮提示数据" />&nbsp;
			<button onClick={
    
    this.showData}>点我提示左侧的数据</button>&nbsp;
			<input onBlur={
    
    this.showData2} type="text" placeholder="失去焦点提示数据" />&nbsp;
		</div>
	)
}

5.2 Classification of form components

The controlled component, the dom of the input class, will maintain things in the state as you input, and take them out of the state when you use them (do not write ref)

state = {
    
    //初始化状态
	username:'', //用户名
	password:'' //密码
}

//保存用户名到状态中
saveUsername = (event)=>{
    
    
	this.setState({
    
    username:event.target.value})
}

//保存密码到状态中
savePassword = (event)=>{
    
    
	this.setState({
    
    password:event.target.value})
}

//表单提交的回调
handleSubmit = (event)=>{
    
    
	event.preventDefault() //阻止表单提交
	const {
    
    username,password} = this.state
	alert(`你输入的用户名是:${
      
      username},你输入的密码是:${
      
      password}`)
}

render(){
    
    
	return(
		<form onSubmit={
    
    this.handleSubmit}>
			用户名:<input onChange={
    
    this.saveUsername} type="text" name="username"/>
			密码:<input onChange={
    
    this.savePassword} type="password" name="password"/>
			<button>登录</button>
		</form>
	)
}

Uncontrolled components, ready-to-use, need ref

handleSubmit = (event)=>{
    
    
	event.preventDefault() //阻止表单提交
	const {
    
    username,password} = this
	alert(`你输入的用户名是:${
      
      username.value},你输入的密码是:${
      
      password.value}`)
}
render(){
    
    
	return(
		<form onSubmit={
    
    this.handleSubmit}>
			用户名:<input ref={
    
    c => this.username = c} type="text" name="username"/>
			密码:<input ref={
    
    c => this.password = c} type="password" name="password"/>
			<button>登录</button>
		</form>
	)
}

6 Higher-order functions and function currying

The arrow function that saves the input data to the state in the controlled component has been written too many times. If there are many things, write a bunch of arrow functions

Write a function saveFormData('username'), and tell it the parameters

onChange ={this.saveFormData('username')}, which means to use the return value of saveFormData as the callback of onChange. It would be great if the return value is a function, and the returned function is the real callback
insert image description here

Don't say whether to write parentheses in function callbacks, the summary is that a function must be handed over to XXX as a callback

saveFormData is a higher-order function (the return value is a function)

//保存表单数据到状态中
saveFormData = (dataType) => {
    
    
	return (event) => {
    
    
		//基本功 对象里面读取变量[]
		this.setState({
    
     [dataType]: event.target.value })
	}
}

render() {
    
    
	return (
		<form onSubmit={
    
    this.handleSubmit}>
			用户名:<input onChange={
    
    this.saveFormData('username')} type="text" name="username" />
			密码:<input onChange={
    
    this.saveFormData('password')} type="password" name="password" />
			<button>登录</button>
		</form>
	)
}

6.1 Higher-order functions

A function is a higher-order function if it meets either of the following two specifications

  • If the A function accepts a function as a parameter, then A can be called a higher-order function

  • If the return value of function A is still a function, then A can be called a higher-order function

Common high-order functions are: Promise, setTimeout, arr.map(), etc.

6.2 Currying of functions

The method of continuing to return the function through the function call realizes the function encoding form of the final unified processing of the accepted parameters

function sum(a){
    
     return (b)=>{
    
    return c=>{
    
     return a+b+c} }}

6.3 Implement event binding without function currying

Use the callback function directly, because it itself returns a function

<input onChange={
    
    event => this.saveFormData('username',event) } type="text" name="username"/>

7 life cycle

Components go through some specific stages from creation to death

React components contain a series of hook functions (life cycle callback functions), which will be called at specific moments

When we define components, we will do specific work in specific life cycle callback functions

7.1 React lifecycle (old)

insert image description here

The calling order of each life cycle hook

Initialization phase : triggered by ReactDOM.render()

  • constructor()

  • componentWillMount()

  • render()

  • componentDidMount() ==>常用

Generally, componentDidMount()do some initialization in the hook, such as: start the timer, send network requests, subscribe to messages, etc.

Update phase : triggered by this.setState() inside the component or the render of the parent component

One of three routes: the setState() route

  • shouldComponentUpdate() The component should be updated. If you don’t write it, the bottom layer will automatically fill in one, and the return value is true. Write it yourself and remember to return the value

  • componentWillUpdate() component will be updated

  • render() ===> 必须使用one of

  • componentDidUpdate() component will be updated

The second of the three routes: forceUpdate() route:

forceUpdate() Forced update: If you want to update the component without modifying the state, skip shouldComponentUpdate()

The third of the three routes: parent component render

Write the componentWillReceiveProps constructor in the subcomponent, which means that the subcomponent will be called when it will receive the passed parameters (note: it will not be called when it is passed for the first time!!!), and it can also receive props parameters

Unmount component : Triggered by ReactDOM.unmountComponentAtNode( 卸载节点上的组件)

  • componentWillUnmount() ===> 常用the component will be unmounted

Generally, do some first things in this hook, such as: closing the timer, unsubscribing, etc.

7.2 React lifecycle (new)

insert image description here

Three constructs UNSAFE_start with: UNSAFE_compinentWillMount(), UNSAFE_componentWillReceiveProps(),UNSAFE_componentWillUpdate()

Notation: Constructors with will want to add UNSAFE_, except that they will be uninstalled (these three are deprecated hooks)

Why add these prefixes: Misunderstood and abused by many people, especially when new asynchronous rendering is released later, this will lead to errors. Adding a prefix is ​​to increase the cost of use

insert image description here

Summary: The old life cycle will discard three hooks, and the new one will add 2 hooks

Initialization phase : Triggered by ReactDOM.render() - initial rendering

  • constructor()

  • getDerivedStateFromProps() Get derived state from Props ===>不常用

getDerivedStateFromProps() is not for instances, so it is defined as a static method in the class, and needs to return a value: state object or null, and the parameters that can be received are props and state

And if the status object is returned, then the update will not be triggered, and the returned object is the main one; if the returned object is not written by itself, but the received props, then this is the explanation for obtaining the derived state from Props, in simple terms After using this hook, the value of the state depends on the passed in props and affects the update

  • render()

  • componentDidMount() ====>常用

Update phase : triggered by this.setState() inside the component or the render of the parent component

  • getDerivedStateFromProps() Get derived state from Props

  • shouldComponentUpdate() The component should be updated

  • render()

  • getSnapshotBeforeUpdate() Take a snapshot before updating

getSnapshotBeforeUpdate() needs to return a snapshot value or null, any value can be used as a snapshot value, return to componentDidUpdate

The meaning of getting a snapshot: click some photos to do some operations before the component is about to be updated to the page

insert image description here

  • componentDidUpdate()

componentDidUpdate() receives three parameters (preProps, preState, snapshotValue), and the last parameter is passed from the getSnapshotBeforeUpdate hook

Unmount component : Triggered by ReactDOM.unmountComponentAtNode()

  • componentWillUnmount() ===>常用

7.3 Important hooks

  • render: Initialize rendering or update rendering calls

  • componentDidMount() : Enable monitoring: send ajax request

  • componentWillUnmount(): do some finishing work, such as: clean up the timer

7.4 Hooks about to be deprecated

  • componentWillMount

  • componentWillReceiveProps

  • componentWillUpdate

ps: There will be a warning when using it now, and the version in the future may need to be prefixed with UNSAFE_, and it may be completely abandoned in the future, so it is not recommended to use

It is speculated that the React team believes that increasing the cost of use will indirectly affect us, let us adapt to the new hook, so add this

8 Keys in React/VUE

Classic interview questions:

  • What is the function of the key in React/VUE (what is the internal principle of the key)

  • Why is it better not to use index for key when traversing the list

8.1 The role of key in virtual DOM

Simply put: the key is the identifier of the virtual DOM object, and the key plays an extremely important role in updating the display

In detail: when the data in the state changes, react will generate according to 新数据it 新的虚拟DOM, and then react will compare it 新虚拟DOMwith 旧虚拟DOMthe diff. The comparison rules are as follows:

  • The same key as the new virtual DOM is found in the old virtual DOM:

      若虚拟DOM中内容没变,直接使用之前的真实DOM
    
      若虚拟DON中的内容变了,则生成新的真实DOM,随后替换掉页面中之前的真实DOM
    
  • The same key as the new virtual DOM was not found in the old virtual DOM:

      根据数据创建新的真实DOM,随后渲染到页面
    

8.2 Possible problems caused by using index as key

  • If you perform operations on the data: add in reverse order, delete in reverse order, etc.:

      会产生没有必要的真实DOM更新 ==>界面效果没问题,但是效率低
    
  • If the structure also contains the DOM of the input class

      会产生错误DOM更新 ===>界面有问题
    
  • Notice:

      如果不存在对数据的逆序添加、逆序删除等破坏顺序操作,仅用于渲染列表用于展示,使用index作为key是没有问题的
    

8.3 How to choose a key during development

  • It is best to use the unique identifier of each piece of data as the key, such as id, mobile phone number, ID number, student number, etc.

  • If it is determined that it is only a simple display of verses, it is also possible to use index

//慢动作回放----使用index索引值作为key
		初始数据:
				{
    
    id:1,name:'小张',age:18},
				{
    
    id:2,name:'小李',age:19},
		初始的虚拟DOM<li key=0>小张---18<input type="text"/></li>
				<li key=1>小李---19<input type="text"/></li>

		更新后的数据:
				{
    
    id:3,name:'小王',age:20},
				{
    
    id:1,name:'小张',age:18},
				{
    
    id:2,name:'小李',age:19},
		更新数据后的虚拟DOM<li key=0>小王---20<input type="text"/></li>
				<li key=1>小张---18<input type="text"/></li>
				<li key=2>小李---19<input type="text"/></li>

-----------------------------------------------------------------

//慢动作回放----使用id唯一标识作为key

		初始数据:
				{
    
    id:1,name:'小张',age:18},
				{
    
    id:2,name:'小李',age:19},
		初始的虚拟DOM<li key=1>小张---18<input type="text"/></li>
				<li key=2>小李---19<input type="text"/></li>

		更新后的数据:
				{
    
    id:3,name:'小王',age:20},
				{
    
    id:1,name:'小张',age:18},
				{
    
    id:2,name:'小李',age:19},
		更新数据后的虚拟DOM<li key=3>小王---20<input type="text"/></li>
				<li key=1>小张---18<input type="text"/></li>
				<li key=2>小李---19<input type="text"/></li>

Guess you like

Origin blog.csdn.net/m0_55644132/article/details/127696715