组件简介
类组件
函数组件
props(外部数据)和state(内部数据)&setState
复杂state
组件,从概念上类似于 JavaScript 函数。它接受任意的入参(即 “props”),并返回用于描述页面展示内容的 React 元素。
组件就是用于组合的物件。在编程中,组件的作用就是使代码具有模块化的意义,能让代码有更好的可读性和维护性。
在React中,定义组件有两种方式,
函数组件和类组件
,不成文的规定:组件首字母大写,元素首字母小写
创建类组件的方式有两种,一种是ES5(过时)的写法,一种是最新的ES6写法。
由于ES5不支持class,才会有这种方式。
import React from 'react';
const A=React.createClass({
render(){
return (
<div>Hello,world!</div>
)
}
})
export default A;
现在,我们一般都使用ES6的方式创建类组件。
import React from 'react';
class B extends React.Component{
/* constructor是初始化的过程 */
constructor(props){
super(props);
}
render(){
return (
<div>Hello,world!</div>
)
}
}
export default B;
函数组件好是好,对比类组件,我们面临两个问题。
函数组件没有state和生命周期函数
没有State:
ReactV16.8.0退出的Hooks API中的useState可以代替。
没有生命周期:
ReactV16.8.0退出的Hooks API中的useEffect可以代替。
函数组件的一些模拟类组件的API
// # 使用箭头函数创建函数组件
const Hello1=(props)=>{
return (
<div>{props.msg}</div>
)
}
// # 简写的箭头函数创建组件
const Hello2=props=><div>{props.msg}</div>
// # 函数function创建
function Hello(props) {
return (
<div>{props.msg}</div>
)
}
<函数名 />或函数名()形式
ReactDOM.render(,document.queryselector('root'));
# 或者
ReactDOM.render(Hello1(),document.queryselector('root'))
props是外部数据,需要在某个组件被用到的时候
外部对他进行传递数据
类组件直接读取属性:
this.props.xxx
函数组件直接读取参数:
props.xxx
props的作用
一般是父组件的函数
props在类组件中的初始化
初始化操作是在
constructor
中完成的。
import React from 'react';
class B extends React.Component{
constructor(props){
super(props)
}
render(){}
}
注意
要么不初始化,即不写constructor
如何要初始化,必须写全套constructor,不写super()会直接报错
this.props
就是外部数据对象的地址
了props在函数组件中的初始化
function App(props) {
return (
<div>{props}</div>
)
}
主类组件A给副类父组件B传递props数据
class App extends React.Component {
render() {
return (
这里是父组件
)
}
}
副类组件B中读取props数据
import React from 'react';
class B extends React.Component{
constructor(props){
super(props)
}
render(){
return (
<>
// 类组件中通过this.props.xxx读取数据
<div>
name:{this.props.name}
<div>
age:{this.props.age}
</div>
</div>
</>
)
}
}
函数主组件给函数父组件传递props数据
function App(props) {
return (
<div>
这里是函数主组件
<Hello name='袁姗姗' age='18'/>
</div>
)
}
函数组件读取props数据
import React from 'react';
const Hello=(props)=>{
return (
<>
// 函数组件通过参数props读取数据
<div>
name:{props.name}
<div>
age:{props.age}
</div>
</div>
</>
)
}
小结
state是内部数据,表示在组件内使用的数据。
数据不可变原理
对比Vue:我们可以直接
this.state.n+=1;this.setState(this.state)
,但是React的初衷则不是让我们这样去操作数据,这是不符合React的设计理念的。React不会像Vue一样直接对data进行代理监听。我们需要的不是直接对原始数据进行操作而是需要产生一个新的对象或值
setState则是一个API专门用来修改state中的值的,它
是一个异步操作
。在setState()括号中最好写一个箭头函数去表示要对数据进行操作的代码。如:setState(i=>i+1)
类组件中使用state&setState()
需要在初始化constructor的时候添加state。
import React from 'react';
class B extends React.Component{
constructor(props){
super(props);
this.state={
n:0,
user:{name:'frank',age:18}
}
}
render(){
<>
n:{this.state.n}
// 点击后触发n+1,但是触发this.state.n+=1页面则不会更新
user:{this.state.user}
>
}
}
函数组件使用state&setState()
需要用到React的一个API:
React.useState(initialState)
以下操作n的setN同样是个异步操作,产生一个新的对象或值
React.useState详解传送门
import React from 'react';
const App=()=>{
const [n,setN]=React.useState(0);
// n就是初始值0,setN为操作初始值0的API
return (
n:{n}
)
}
小结
this.state.n+=1无效?
其实n值已经改变了,但是React不会去触发UI更新。只有调用setState才会触发更新,因为React没有像Vue一样对state进行代理监听
。setState是异步操作,即异步更新渲染页面
,推荐使用setState(函数)
的方式。函数表示对n的操作,如:setState(i=>i+1)
不可变数据
的思想,它不希望我们修改旧的state。常用代码:setState({n:this.state.n+1})
React.useState(initialState)
来模拟使用state,也要通过setX()来更新UI,也是一个异步操作
。什么叫复杂的state?就是当我们的state中数据不只有n时,如同时存在n和m或一个对象user时。当我们只对一个数据进行修改时,会发生什么呢?你会发现另外的数据如m会被置空
这是因为setState只会shallow merge。它只
合并第一层的数据
类组件中有n和m
以下情况中,只set n或者m的值,另外一个值不会被置空。
import React from 'react';
class A extends React.Component {
constructor() {
super();
this.state = {
n: 0,
m: 0
};
}
addN() {
this.setState({ n: this.state.n + 1 });
// m 会被覆盖为 undefined 吗?
}
addM() {
this.setState({ m: this.state.m + 1 });
// n 会被覆盖为 undefined 吗?
}
render() {
return (
<div>
n: {this.state.n}
<button onClick={() => this.addN()}>n+1</button>
m: {this.state.m}
<button onClick={() => this.addM()}>m+1</button>
</div>
);
}
}
类组件中有对象user{}
在以下拥有user对象时,你会发现,第一层属性n和m不会被置空,但我只修改user里面的name时,age却被置空了。
解决方案:
使用...操作符将原先数据拷贝过来或者Object.assign,再进行修改
import React from 'react';
class B extends React.Component {
constructor() {
super();
this.state = {
n: 0,
m: 0,
user: {
name: "frank",
age: 18
}
};
}
addN() {
this.setState({ n: this.state.n + 1 });
// m 会被覆盖为 undefined 吗?
}
addM() {
this.setState({ m: this.state.m + 1 });
// n 会被覆盖为 undefined 吗?
}
changeUser() {
this.setState({
// m 和 n 不会被置空
user: {
// 要想age不被置空=> ...this.state.user;
name: "jack"
// age 被置空
}
});
}
render() {
return (
<div>
n: {this.state.n}
<button onClick={() => this.addN()}>n+1</button>
m: {this.state.m}
<button onClick={() => this.addM()}>m+1</button>
<hr />
<div>user.name: {this.state.user.name}</div>
<div>user.age: {this.state.user.age}</div>
<button onClick={() => this.changeUser()}>change user</button>
</div>
);
}
}
在函数组件中React不会自动合并第一层属性。
函数组件里面有n和m
import React from 'react';
const C = () => {
const [n, setN] = React.useState(0);
const [m, setM] = React.useState(0);
return (
<div>
n:{n}
<button onClick={() => setN(i=>i+1)}>n+1</button>
m:{m}
<button onClick={() => setM(i=>i+1)}>m+1</button>
</div>
);
};
函数组件中对象形式
只改变对象中一个数据的值,另外的数据会被置空。
解决方案:
使用...操作符拷贝原先的数据或Object.assign
import React from 'react';
const D = () => {
const [state, setState] = React.useState({n:0,m:0});
return (
<div>
n:{n}
/* 注意,这个按钮会将m的值置空 */
<button onClick={() => setState({n:state.n+1})}>n+1</button>
m:{m}
<button onClick={() => setM({...state,n:state.n+1})}>m+1</button>
</div>
);
};
小结
...操作符或者Object.assign中转。