组件(Component)
1、组件:能跟其他物件组合起来的物件
2、就目前而言,一个返回React元素的“函数”就是组件
3、在Vue里,一个构造选项就可以表示一个组件
元素与组件
元素:const div = React.createElement('div',...)
//这是一个React元素(d是小写的)
组件:const Div = ()=>React.createElement('div'..)
//这是一个React组件(D是大写的)
React 的两种组件
1、类组件
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>
);
}
}
类组件注意事项:
- this.state.n += 1 无效?
//其实n已经变了,UI没自动更新,
//调用 setState 才会触发更新(异步更新,这里复制原有n,得到一个新的n)
//React不不像Vue一直监听data 所以不会更新 - setState 会异步更新UI
//setState 之后,state不会立马改变,立马读会失败
//推荐使用setState(函数)
2、函数组件
const Grandson = () => {
const [n, setN] = React.useState(0);
return (
<div className="Grandson">
孙子n:{n}
<button onClick={() => setN(n + 1)}>+1</button>
</div>
);
};
函数组件注意事项:
没有 this,一律使用参数和变量
组件使用方法:
<Welcome />
会被翻译成什么<div />
会被翻译为React.createElement('div')<Welcome />
翻译为React.createElement(Welcome)可以用babel online直接翻译给你看
React.createElement的逻辑
如果传入一个字符串div, 则会创建div
如果传入一个函数,则会调用该函数,获取其返回值
如果传入一个类,则在类前面加个new(这会导致执行constructor), 获取一个组件对象,然后调用对象的render方法,获取其返回值
props - 外部数据
const root =document.getElementById('root')
const React = window.React
const ReactDOM = window.ReactDOM
// import React from 'react'
// import ReactDOM from 'react-dom'
function App(){
return(
<div className="App">
爸爸
<Son x="吃饭"/>
</div>
)
}
class Son extends React.Component{
constructor(){
super()
this.state = {
n:0
}
}
add(){
this.setState({n:this.state.n+1})
}
render(){
return(
<div>
儿子 n:{this.state.n},{this.props.x} //---------类组件直接用this.props接收值
<button onClick={()=>this.add()}> +1 </button>
<Grandson y="睡觉"/>
</div>
)
}
}
const Grandson =(props) =>{
const [n,setN] = React.useState(0)
return(
<div>
孙子 n:{n},{props.x}//---------函数组件接收一个props的参数不需要this就可以接收值
<button onClick={()=>setN(n+1)}> +1 </button>
</div>
)
}
ReactDOM.render(App(),root);
state - 内部数据
- 链接
- 类组件用this.state读,this.setState写
- 函数组件用useState返回数组,第一项读,第二项写
- 关注数据的时候我们需要关注,初始化,怎么读,怎么写
- 直接改state的n,ui不会刷新,vue里面是对data做了劫持,必须要用setState,
- 另外不推荐对象局部改变了,又原封不动的给setState,效果是ok的但是一般推荐要产生一个新的对象
- 这就叫做数据不可变,以前的数据不要改,要改就产生一个新的对象
this.setState({ n: this.state.n + 1 });
- 牛x的前端用setState(函数),这样写的好处,打印n的值发现不对,因为setState是异步的更新ui的过程,好处就是避免异步造成的误解
this.setState((state)=>{
return {n: state.n+1}
})
// 如果用这种写法就很清楚的知道旧的state和新的state
// 打印n
this.setState(state=>{
const n = state.n + 1
console.log(n)
return {n}
})
setState 注意事项
- this.state.n += 1无效?
- 其实n已经改变了,只不过UI不会自动更新而已
- 调用setState才会触发UI更新(异步更新)
- 因为React没有像Vue监听data一样监听state
- setState会异步更新UI
- setState之后,state不会马上改变,立马读state会失败
- 更推荐的方式是setState(函数),函数接受一个旧的state返回一个新的state
- this.setState(this.state)不推荐
- React希望我们不要修改旧state(不可变数据)
- 常用代码: setState({n: state.n + 1})
- 总结,这是一种理念(函数式)
函数组件注意事项
- 跟类组件类似的地方,也要通过setX(新值)来更新UI
- 跟类组件不同的地方,没有this,一律用参数和变量
复杂的state
如果state里不止有n怎么办
(类组件里有n和m)[https://codesandbox.io/s/friendly-aryabhata-eq24y]
(函数组件里有n和m)[https://codesandbox.io/s/funny-montalcini-wszxe]
函数组件另一种(不推荐的写法)[https://codesandbox.io/s/distracted-taussig-dmkjz],写完会发现m被置空
总结复杂的state
- 类组件的setState会自动合并第一层属性
- (但是不会合并第二层属性)[https://codesandbox.io/s/dreamy-pike-w9je2],需要使用(Object.assign)[https://codesandbox.io/s/zen-margulis-gjr7t],或者(...操作符)[https://codesandbox.io/s/dawn-sound-rwyxv]
- 函数组件的setX则完全不会合并state,需要使用Object.assign,或者(...操作符)
- ...在react是经常使用的
事件绑定
类组件的事件绑定
<button onClick={() => this.addN()}>n + 1</button>
// 传一个函数给onClick即可,注意C大写
// 思考一个问题,下面这样写行不行
<button onClick={this.addN}>n + 1</button>
// 有问题,这样使得this.addN里的this变成window
<button onClick={this.addN.bind(this)}>n+1</button>
// 这样写是可以的,因为它返回一个绑定了当前this的新函数
// 但是这样写太麻烦,你不如第一种
// 但是第一种写法依然太长,可用this._addN = ()=> this.addN()
// 给箭头函数取个名字,然后写成,在构造函数中赋值
<button onClick={this._addN}>n+1</button>
// 给箭头函数取个名字,然后写成
<button onClick={this._addN}>n + 1</button>
// 这样又不如写成
constructor() {
this.addN = ()=> this.setState({n: this.state.n + 1})
}
render(){
return <button onClick={this.addN}>n + 1</button>
}
但这样写不如声明addN结构清晰
最终的方案,类组件的事件绑定最好的方法
class Son extends React.Component {
addN = () => this.setState({n: this.state.n + 1});
render() {
return <button onClick={this.addN}>n+1</button>
}
}