1 元素与组件
const div = React.createElement('div',...)
复制代码
这是一个React元素 (d小写)
const Div = () => React.createElement('div'...)
复制代码
这是一个React组件(D大写)
什么是组件?
目前而言,一个返回React元素的函数就是组件
2 React 两种组件
-
函数组件
function Welcome(props){ return <h1> hello, {props.name} </h1>; } 使用方法: <Welcome name = "frank"/> 复制代码
-
类组件
class Welcome extends React.Component{
render(){
return <h1> Hello,{this.props.name}</h1>
}
}
使用方法:
<Welcome name = "frank"/>
复制代码
3 <Welcome/>
-
会被翻译成什么
<div />
会被翻译成React.createElement('div')
<Welcome />
会被翻译成React.createElement(Welcome)
-
React.createElement的逻辑
-
如果传入一个
字符串
‘div’,则会创建一个div(虚拟dom) -
如果传入一个
函数
,则会调用该函数,获取其返回值
-
如果传入一个
类
,则在类前面价格new
(这回导致执行constructor),获取一个组件对象
,然后调用对象的render方法,获取其返回值
class Welcome extends React.Component{ constructor(){ super() this.state = {n : 0} // 初始化 } render(){ return <h1> Hello,{this.props.name}</h1> } } new Welcome() <Welcome name = "frank"/> 复制代码
-
4 添加props(外部数据)
props不能写,只能读!
import React from "react";
import ReactDOM from "react-dom";
import "./styles.css";
function App() {
return (
<div className="App">
爸爸
<Son />
{/* 函数里的子标签可以用class */}
</div>
);
}
class Son extends React.Component {
// 初始化
constructor() {
super();
this.state = {
n: 0
};
}
add() {
// this.state.n += 1 为什么不行
this.setState({ n: this.state.n + 1 });
}
render() {
return (
<div className="Son">
儿子 n: {this.state.n}
<button onClick={() => this.add()}>+1</button>
<Grandson />
</div>
);
}
}
const Grandson = () => {
// 析构赋值
// const array = React.useState(0);
// const n = array[0]
// const setN = array[1] 得到一个新的n,而不是改变原有的n
const [n, setN] = React.useState(0);
return (
<div className="Grandson">
孙子 n:{n}
<button onClick={() => setN(n + 1)}>+1</button>
</div>
);
};
const rootElement = document.getElementById("root");
ReactDOM.render(<App />, rootElement);
// App 是个函数,调用App(), 创建一个div放到root里
复制代码
<Son messageForSon = "儿子你好" />
<Son messageForSon = {a} /> // 变量
<Son messageForSon = "{hi}" /> // 字符串
复制代码
- 类组件直接读取属性 this.props.xxx
[{this.props.messageForSon}]
复制代码
- 函数组件直接读取参数 props.xxx
[{props.messageForGrandson}]
复制代码
5 添加state(内部数据)
(vue里的data)
-
在类组件中使用state
用this.state读,用this.setState写
class Son extends React.Component {
constructor() {
super();
this.state = {
n: 0
};
}
// 初始化数据
add() {
// react不会实时监听,所以this.state.n += 1 不行
// 写法1 产生一个新的对象(异步)
this.setState({ n: this.state.n + 1 });
// 写法2 传入一个旧的state(异步)
this.setState((state) => {
return {n : state.n + 1}
})
// 写法3
this.setState((state) => {
const n = state.n + 1
return {n}
})
}
render() {
return (
<div className="Son">
儿子 n: {this.state.n}
{/* 读 state的值 this.state.n */}
<button onClick={() => this.add()}>+1</button>
{/* 写 state的值 通过this.add()调用this.setState */}
<Grandson />
</div>
);
}
}
复制代码
注意事项:
-
this. state.n+=1无效?
其实n已经改変了,只不过UI不会自动更新而已调用 setstateオ会触发UI更新(异步更新)
因为 React没有像Vue监听data一样监听
-
setstate会异步更新Ui
setState之后, state不会马上改变,立马读state会失败 更推荐的方式是 setstate(函数)
-
this. setstate(this. state)不推荐
React希望我们不要修改旧 state(不可变数据) 常用代码: setstate((n: state.n+1)
-
在函数组件中使用state
用useState给一个初始值返回数组,第一项读,第二项写
const Grandson = () => {
// 初始值 const [读接口, 写接口]
const [n, setN] = React.useState(0);
return (
<div className="Grandson">
{/* 读 state的值 {n} */}
孙子 n:{n}
{/* 写 state的值 setN setN不会改变n,而是异步的产生一个新的n*/}
<button onClick={() => setN(n + 1)}>+1</button>
</div>
);
};
复制代码
注意事项:
-
跟类组件类似的地方
也要通过setX(新值)来更新UI
-
跟类组件不同的地方
没有this,一律用参数和变量
6 复杂的state怎么处理
-
类组件里有n和m
class Son extends React.Component { constructor() { super(); this.state = { n: 0, m: 0 }; } addN() { this.setState({ n: this.state.n + 1 }); // m 会被覆盖为 undefined 吗? // class组件中的setState如果只对其其中一部分进行修改,其他部分会自动沿用上一次的值 } addM() { this.setState({ m: this.state.m + 1 }); // n 会被覆盖为 undefined 吗? } render() { return ( <div className="Son"> 儿子 n: {this.state.n} <button onClick={() => this.addN()}>n+1</button> m: {this.state.m} <button onClick={() => this.addM()}>m+1</button> <Grandson /> </div> ); } } 复制代码
类组件的setState会自动合并第一层属性,但是不会合并第二层属性,有两层属性怎么办?
用
...
操作符或者Object.assign
class Son extends React.Component { constructor() { super(); this.state = { n: 0, m: 0, user: { name: "frank", age: 18 } }; } changeUser() { this.setState({ // m 和 n 不会被置空 user: { ...this.state.user, // const user = Object.assign({},this.state.user); // 等价于 const user = {...this.state.user} name: "jack" // age 被置空 } // 如果不用... 则 {name:'frank',age:18} => {name:'jack'} }); } render() { return ( <div className="Son"> <button onClick={() => this.changeUser()}>change user</button> </div> ); } } 复制代码
-
函数组件里有n和m
const Grandson = () => { const [n, setN] = React.useState(0); const [m, setM] = React.useState(0); return ( <div className="Grandson"> 孙子 n:{n} <button onClick={() => setN(n + 1)}>n+1</button> m:{m} <button onClick={() => setM(m + 1)}>m+1</button> </div> ); }; 复制代码
函数组件一般不写setState,因为多个属性不会自动合并
7 事件绑定
-
类组件的事件绑定
- 最好这么写
<button onClick = {() => this.addN()}> n+1 </button> 复制代码
传一个函数给onClick即可
可改写成
this._addN = () => this.addN() <button onClick = {this._addN}> n+1 </button> 复制代码
最终写法
class Son extends React.Component{ addN = () => this.setState({n: this.state.n + 1}); render(){ return <button onClick={this.addN}>n+1</button> } } 复制代码
- 不能这么写
<button onClick = {this.addN}> n+1 </button> // button.onClick.call(null, event) // this === null =>window 复制代码
这样会使得 this.addN 里的 this 变成 window
- 还有一种写法
<button onClick = {this.addN.bind(this)}> n+1 </button> 复制代码
它返回了一个绑定了当前this的新函数
-
函数组件的事件绑定
function App() {
const [n, setN] = React.useState(0)
return (
<div className="App">
n: {n}
<button onClick={()=>setN(n+1)}>+1</button>
</div>
);
}
复制代码