1. 元素与组件
const div = React.createElement( 'div',...) // 这是一个 React 元素
const Div = ()=>React.createElement( 'div' ..) // 这是一个 React 组件
什么是组件?
- 能跟其他物件组合起来的物件(零件),就是组件
- 组件并没有明确的定义,靠感觉理解就行
- 就目前而言,一个返回React元素的函数就是组件
- 在Vue里,一个构造选项就可以表示一个组件
2. React的两种组件及数据的读写
1.函数组件
function Welcome(props){
return <h1>Hello,{props.name}</h1>;
}
使用方法
<Welcome name='frank' />
2.类组件
class Welcome extends React.Component {
render(){
return <h1>Hello,{this.props.name}</h1>
}
}
使用方法
<Welcome name='frank' />
组件的解析过程
上面的 welcome 会被翻译成什么呢?
<div /> <!-- 会被翻译为React.createElement('div') -->
<Welcome /> <!-- 翻译为React.createElement(Welcome) -->
React.createElement 的逻辑
- 如果传入一个字符串'div'(原生标签),则会创建一个div(虚拟DOM)
- 如果传入一个函数,则会调用该函数,获取其返回值
- 如果传入一个类,则在类前面加个new(这会导致执行 constructor),获取一个组件对象,然后调用对象的 render 方法,获取其返回值
// React 小试牛刀
import React from "react";
import ReactDOM from "react-dom";
import "./styles.css";
function App() {
return (
<div className='App'>
爸爸
<Son/>
</div>
);
}
class Son extends React.Component {
constructor() {
super();
this.state = {
n: 0
}
}
add() {
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 [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);
3. React组件使用 props
-
添加 props 外部数据 不可直接写
类组件 直接读取属性 this.props.xxx
<Son props='你好啊'/> // 直接添加属性 变量则使用{xxx} class Son extends React.Component { constructor() { super(); this.state = { n: 0 } } add() { this.setState({n: this.state.n + 1}); } render() { return ( <div className='Son'> 儿子 n:{this.state.n} 他说:{this.props.props} // 读取属性 <button onClick={() => this.add()}>+1</button> <Grandson/> </div> ); } }
函数组件读取参数 props.xxx
<Grandson sunzi='你是孙子'/> // 参数传递同类组件 const Grandson = (props) => { // 函数组件使用函数参数来接收props数据 const [n, setN] = React.useState(0) return ( <div className='Grandson'> 孙子 n:{n} 他说:{props.sunzi} // 接收数据 <button onClick={() => setN(n + 1)}>+1</button> </div> ) }
-
添加 state 内部数据
类组件用 this.state 读,this.setState 写
class Son extends React.Component { constructor() { super(); this.state = { // 必须写在 super 下面声明state n: 0 // 初始化 } } add() { this.setState({n: this.state.n + 1}); // setState 写属性 // 只有使用 setState 才能更新UI 而且是异步更新数据 // React中不提倡改变之前的属性 要产生新的对象 } render() { return ( <div className='Son'> 儿子 n:{this.state.n} // 读数据 他说:{this.props.props} <button onClick={() => this.add()}>+1</button> <Grandson sunzi='你是孙子'/> </div> ); } }
setState 高级用法 函数写法
add() { // 还是上面的 add 方法 this.setState((state) => { // setState 函数写法 return { n:state.n + 1 } }); }
函数组件 使用 useState 返回数组,第一项读,第二项写
const Grandson = (props) => { const [n, setN] = React.useState(0) //初始值 析构赋值写法 return ( <div className='Grandson'> 孙子 n:{n} 他说:{props.sunzi} <button onClick={() => setN(n + 1)}>+1</button> </div> ) // setN 不会改变 n 的值,只会产生一个新的n }
-
类组件的注意事项
- 组件中直接使用 this.state.n + = 1是不会更新 UI 的
- 其实n已经改变,只不过UI不会自动更新而已
- 调用setState才会触发UI更新(异步更新)
- 因为 React没有像 Vue 监听data一样监听 state
- setState之后,state不会马上改变,立马读state会失败
- 更推荐的方式是setState(函数)
- 不推荐 this.setState(this.state) 这种写法
- React 希望我们不要修改旧 state(不可变数据)
- 常用代码:setState({n: state.n+1})
复杂情况写的的 state
类组件的 setState 会自动合并旧的数据
class Son extends React.Component {
constructor() {
super();
this.state = {
n: 0, // 这里有两个值 m n
m:0
}
}
addN() {
this.setState({n: this.state.n + 1}); // 这里修改n时,react会把其他的数据(这里指的是m)沿用旧的值 但也只是第一层数据,第二层数据不会合并
}
addM() {
this.setState({m: this.state.m + 1});
}
render() {
return (
<div className='Son'>
儿子 n:{this.state.n}
他说:{this.props.props}
<button onClick={() => this.addN()}>+1</button>
m的值:{this.state.m}
<button onClick={() => this.addM()}>+1</button>
<Grandson sunzi='你是孙子'/>
</div>
);
}
}
函数组件的 setState 不会自动合并旧的数据
// 函数组件第一层也不会合并
const Grandson = (props) => {
const [state, setState] = React.useState({
m: 0,
n: 0
})
return (
<div className='Grandson'>
孙子 n:{state.n}
<button onClick={() => setState({n: state.n + 1})}>n+1</button>
孙子 m:{state.m}
<button onClick={() => setState({m: state.m + 1})}>m+1</button>
</div>
)
}
// 点击按钮 另一个数据会变成 undefined 从而不显示
多层数据的合并方法一------使用 ... 运算符
// 应该先拷贝之前的数据
const Grandson = (props) => {
const [state, setState] = React.useState({
m: 0,
n: 0
})
return (
<div className='Grandson'>
孙子 n:{state.n}
<button onClick={() => setState({...state,n: state.n + 1})}>n+1</button>
孙子 m:{state.m}
<button onClick={() => setState({...state,m: state.m + 1})}>m+1</button>
</div>
)
}
多层数据的合并方法二------使用 Object.assign
changeUser(){
const user = Object.assign({},this.state.user)
user.name = '大欧'
this.setState({
// 这里 m m 不会被置空
user:user
})
}
// 拷贝了user对象
3. 事件绑定
类组件的事件绑定
<button onClick = {() => this.addN()}> n+1 </button>
传一个函数给onClick即可,注意C大写
思考一个问题,下面这样写行不行
<button onClick= (this.addN) >n+1</button> <!-- 错误写法 -->
有问题,上面这样会使得 this.addN函数体里面的 this 变成 window
目前类组件绑定事件的最好写法:
addN = () => this.setState({n:this.state.n + 1}) // 定义函数名 addN
// 这样写是实例对象上面的方法
<button onClick = {this.addN}> n+1 </button> // 使用该函数