学习内容:
-
- 类组件和函数组件
-
- 如何使用props和state
-
- 如何绑定事件
-
- 复习this和两个面试题
一、类组件和函数组件
前提知识:
1.什么是组件?
可以和其他东西结合起来的物件
2.元素与组件
- element component
- 一个返回React元素的函数就是组件
- React中的元素: 变量名小写
const app = React.createElement('div',……)
- React中的组件 : 变量名大写
const App = ()=> React.createElement('div',……)
React组件初体验
React中有两种组件:一个是类组件,一个是函数组件
- 函数组件
//定义函数组件
function Welcome(props){
return (<h1>Halo,{props.name}</h1>)
}
//使用组件
<Welcome name='Yuyuan'/>
- 类组件
//定义类组件
Class Welcome extends React.Component{
render(){
return (<h1>Halo,{this.props.name}</h1>)
}
}
//使用类组件
<Welcome name='Yuyuan'/>
对<Welcome />的理解
<Welcome />是XML语法,它对应的JS语法是什么样子?
1. 借助工具进行翻译
转义代码:
<div></div>;
<div className='red'>halo</div>;
<App name='Yuyuan' />;
const App_01 = ()=>{
return <h1>halo</h1>
} ;
const App_02 = (props)=>{
return (<h1>halo,{props.name}</h1>)
}
<App_02 name='Yuyuan' />;
;
class App_03 extends React.Component{
constructor(){
super()
this.state = {n:0}
}
render(){
return <div>hi</div>
}
}
<App_03 name='hi'/>
2. 转义结果解释
- 首先,
<div />,<Welcome />都会被翻译成React.createElement('div or Welcome') - 然后对于React.createElement而言,
- 如果传入一个字符串'div',则会创建一个div
- 如果传入一个函数,则会调用该函数获取其返回值
- 如果传入一个类,则会在类前面加个new(这会导致执行construcor),获取一个组件对象,然后调用对象的render方法,获取其返回值
组件使用的小练习
前置知识:
- 函数组件和类组件的定义使用,React虚拟DOM元素的定义使用
- {}的使用
- 如何使用props(外部数据,如何使用state(内部数据 (见下文)
- 析构赋值
const [n,setN]=React.useState(0)等价于arr=React.useState(0);n=arr[0];setN=arr[1]
代码复写:
import React from 'react';
import ReactDOM from 'react-dom';
function App(){
return (<div className='grandpa'>爷爷<Dad /></div>)
}
class Dad extends Component{
constructor(){
super()
this.state = {n:0}
}
add(){
this.setState({n:this.state.n+1})
}
render(){
<div className='dad'>爸爸 n : {this.state.n}
<button onClick={()=>this.add()}>
+1
</button>
<Son />
</div>
}
}
Son = ()=>{
const [n,setN] = React.useState(0)
return (
<div className='son'>儿子 n :{n}
<button onClick={()=>setN(n+1)>
+1
</button>
</div>
)
}
ReactDOM.render(<App />,document.getElementById('root'))
bug:
- js中使用jsx报错 :/src/index.js: Support for the experimental syntax
- 错误原因:弄混了
<scipt>引入法和webpack自带法,scr引入只能写在scr type中,webpack自带的babel-loader可以在js中直接写。 - 解决方法:在codesanbox中新建一个react文件就行了,import引入React和ReactDOM
- 错误原因:弄混了
二、如何使用props和state
1. 添加props外部数据
- 定义外部数据:
<变量名>=<数据> props_01='你爷爷的'数据 可以是字符串也可以是{js变量},是在组件被使用的时候定义的。 - 使用外部数据:class中使用前面加上this,
props.<变量名> props.props_01
import React from 'react';
import ReactDOM from 'react-dom';
function App(){
return (<div className='grandpa'>爷爷<Dad props_01='你爷爷的'/></div>)
}
class Dad extends React.Component{
render(){
return (
<div className='dad'>爸爸,{this.props.props_01}<Son props_02={1+1} /></div>
)
}
}
const Son = (props)=>{
return (
<div className='son'>儿子,{props.props_02}</div>
)
}
2. 添加state内部数据
- state的使用分为类组件和函数组件
- 案例就是本文中的React组件使用案例中的例子,此处对state的使用进行解释和说明。
A.在类组件中
a.使用state:
- state的初始化
this.state={n:0}在constructor中,super()之后 - state的读取
{this.state.n} - state的写入
this.setState({n:this.state.n+1})this.setState(state=>{return {n : state.n+1}})
b.注意事项:
- React无法自动监听n的变化,所以更改数据时,
this.state.n+=1是无效代码,要使用setState去监听 - setState不会立刻刷新UI,而是会异步刷新,因此立刻去读state中的值不会是最新的值,推荐使用setState(函数)方法
- setState参数不要只写变化后的值,推荐写对象,这样就不会更改原来数据的值,而是会新分配地址去存储,
this.setState({n:this.state.n+1})
B.在函数组件中
a.使用state
const [n,useN]=React.useState(0)
- React.useState接受的参数是初始值,返回一个数组,数组第一项读,第二项写。
n:{n},onClick={()=>useN(n+1)}
b.注意事项:
- 更新UI,需要通过useState返回的第二项
- 不使用this
C.复杂state的处理
a.类组件中的复杂state
- 初始化:
this.state={n:0,m:0},写一个state的初始化就行了 - 读值:和简单的state读值一样
- 写入:另外写一个函数写另外的state如何变化
- diff检测到变化比较时,第一层属性不会合并,更改m不会覆盖n的值为undefined
- 相当于
return { ...state, m: state.m + 2 };这样继承了n值
b.函数组件中的复杂state
- 可以定义多个React.useState来写复杂组件,如案例中的son函数部分
const Son = () => {
const [n, stN] = React.useState(0);
const [m, stM] = React.useState(0);
return (
<div className="son">
儿子 n :{n}
<button onClick={() => stN(n + 1)}>+1</button>m : {m}
<button onClick={() => stM(m + 3)}>+3</button>
</div>
);
};
- 可以只定义一个React.useState,但是读值要指定,写值要继承。这里不会智能合并。
const Grandson = () => {
const [state, setState] = React.useState({ n: 0, m: 0 });
return (
<div className="grandson">
老弟 n :{state.n}
<button onClick={() => setState({ ...state, n: state.n + 1 })}>+1</button>
m : {state.m}
<button onClick={() => setState({ ...state, m: state.m + 5 })}>+5</button>
</div>
);
};
c.两层属性在类组件和函数组件之中都不会自动合并
//初始化
this.state = {
n:0,
m:0;
user:{name:'yuyuan',age:18}
}
//改值不会继承 错误改值
change(){
this.setState({user:{name:'fuck'}})
}
//改值第一种方法 继承
changeClass(){
this.setState({...this.state.user,user:{name:'fuck01'}})
}
//改值第二种方法 复制
changeAss(){
const user = Object.assign({},this.state.user)
user.name='fuck03'
this.setState({user:user})
}
error:
总是在各种语法上写出各种错误,例如:
this.setState({...this.state.user,user:{name:'fuck01'}})这一句可以写出n个错误:
- 把继承的不和user写在一级,继承的不写this
- 里面要更改的user写this
- user:{name}要写成user.name
- 外面的函数硬要写成
return this.setState(),然后setState中写函数又忘记写return - ……
三、React绑定事件的各种写法
- 用函数组件直接躲过与this的交锋
- 绑定事件最麻烦的是this会绕来绕去,但是我目前this还绕不清楚,因此,直接写结论吧。
- (this博客待写) this学习推荐文档
//定义函数
addMN = ()=>{
this.setState({n:this.state.n+1})
}
//绑定事件
onClick={this.addMN}
裹脚布:
四、this的面试题
解题要点:
- 先动手,不要用脑袋去理解,理解也理解不了
- 动手,遇到函数调用直接改写成call形式,null的this即为window。
题1:
题2:
this面试题2.png 10 2