React类组件和函数组件

165 阅读7分钟

Element 和 Component

const div = React.createElement('div',......)            React元素,d小写
const Div = ()=>React.createElement('div',......)        React组件,D大写

在React里,只要一个函数返回一个React元素,就是React组件
是不是元素是看自己本身,而不是后代,div就是Element,就算里面有个span,div还是元素

函数组件

function Welcome(props){
  return <h1>Hello, {props.name}</h1>;
}
使用方法
<Welcome name="frank"/>
函数组件,Welcome(函数名),接收一个外部数据(props)外部数据放在一个参数里,是一个参数的变量
返回一个标签,自动变成React.createElement('h1',...)
当成标签使用,自动变成key,value

类组件

class Welcome extends React.Component{
  render(){
    return <h1>Hello, {this.props.name}<h1>
  }
}
使用方法
<Welcome name="frank"/>
extends(继承),继承的没有变量,在this上拿到props

<Welcome />        React.createElement(Welcome)
<div />            React.createElement('div')
原生元素写字符串,自定义写函数名,babel online(翻译React代码网站)

React.createElement 的逻辑
如果传入一个字符串'div',则会创建一个div
如果传入一个函数,则会调用该函数,获取其返回值
如果传入一个类,则在类前面加个 new
这会导致执行constructor,获取一个组件对象,然后调用对象的render方法,获取其返回值

class Welcome extends React.Component{
  constructor(){                         //new对应的函数,初始化在这里面写
    super()                              //必须写
    this.state = {n:0}                   //在构造函数中写初始化 state
  }
  render(){
    return <div>hi</div>
  }
}
new Welcome()

实例,使用类和函数实现加1功能

function App(){
  return (
    <div className="App">
      爸爸
      <Son />
    </div>
  );
}
class Son extends React.Component{
  constructor() {               //初始化
    super();
    this.state = {              //state是vue里的data 数据
      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);  //n代表0,初始值,n是读 ,setn写
  return (                              //setn之后得到一个新的n,不是在原来的n上面修改
    <div className="Grandson">
      孙子 n:{n}
      <button onClick={() => setN(n+1)}>+1</button>
    </div>  
  );
};

const rootElement = document.getElementById("root");
ReactDOM.render(<App />, rootElement);

析构函数

const [n, setN] = React.useState(0);     析构函数,等价于下面代码
声明一个state,初始值为0,用n代表0,用setN修改n,setN之后得到一个新的n,不修改原来的n

const array = React.useState(0);          声明一个数组,初始值为0
const n = array[0]                        读这个n,n是数组下标0
const setN = array[1]                     写这个n,等于数组的下标1

添加prpos(外部数据)

类组件直接读取属性     this.props.xxx
函数组件直接读取参数   props.xxx

类组件外部数据写法:   <Grandson messageForGrandson="孙子你好" />
函数组件外部数据写法: const Grandson = (props) => {}
外部数据会自动变成第一个对象,放到第一个参数上,props可以叫任意名字

function App() {
  return (
    <div className="App">
      爸爸
      <Son messageForSon="儿子你好" />
    </div>
  );
}

class Son extends React.Component {
  render() {
    return (
      <div className="Son">
        我是儿子,我爸对我说[{this.props.messageForSon}]
        <Grandson messageForGrandson="孙子你好" />
      </div>  
    );
  }
}
const Grandson = props => {
  return (
    <div className="Grandson">
      我是孙子,我爸对我说[{props.messageForGrandson}]
    </div>  
  );
};
const rootElement = document.getElementById("root");
ReactDOM.render(<App />, rootElement);

添加 state(内部数据)

类组件用   this.state读,this.setState写
函数组件用 useSate返回数组,第一项读,第二项写

类组件初始化state
class Son extends React.Component{
  constructor(){                //new对应的函数,初始化在这里面写
    super()                     //必须写
    this.state = {n:0}          //在构造函数中写初始化 state,this.state等于一个对象,值为n:0
  }
  render(){
    return <div>hi</div>
  }
}
new Welcome()

add(){
    this.setState((state) =>{
      return {
        n:state.n + 1
      }
    })
  }

初始化,在constructor里super下面初始化state
读,    用this.state.n读
写,    调用add()函数
setState是一个异步更新UI的过程,先把n给React,n是之前的n,所有代码都执行完才会更新n
用函数写法会传新的n,同步更新,用this.setState写,写的时候使用新的对象,不要在原有的对象上修改

函数组件用state
const Grandson = () => {
  const [n, setN] = React.useState(0);  //要使用state,使用React.useState(0)给一个初始值0
  return (
    <div className="Grandson">
      孙子 n:{n}                                       //读,使用时
      <button onClick={()=> setN(n + 1)}>+1</button>   //写,直接setN(n + 1)
    </div>                                             //setN不会改变n,会产生一个新的n 
  );
}
返回数组的第一项,就是用来读的n,第二项用来写的

完整代码
function App() {
  return (
    <div className="App">
      爸爸
      <Son />
    </div>
  );
}
class Son extends React.Component{
  constructor(){
    super();
    this.state = {
      n:0
    };
  }
  add(){
    this.setState((state) =>{
      return {
        n: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);

类组件 会等一会改变n
函数组件永远不会改变n

组件注意事项

类组件
this.state.n += 1 无效
其实n已经改变了,只不过UI不会自动更新而已
调用setState才会触发更新UI(异步更新),因为React没有像Vue监听data一样监听state

setState会异步更新UI,setState之后,state不会马上改变,立马读state会失败
推荐方式是setState(函数):setState({n : state.n + 1})

函数组件
跟类组件类似的地方,通过setN(新值)来更新UI
跟类组件不同的地方,没有this,一律用参数和变量

复杂 state(state里不只有n)

类组件里有n和m

class组件里的setState,如果对其中一部分修改,其它部分会自动沿用上一次的值,而不会被undefined覆盖

addN(){this.setState({...this.state, n: this.state.n + 1})}
...this.state三个点复制之前的值

const user = Object.addign({}, this.state.user)
{}是一个新对象,把之前的属性(this.state.user)复制到新对象上,然后等于user;等价于
const user = {}                                                              
const user = {...this.state.user}

function App(){
  return (
    <div className="App">
    爸爸
    <Son />
    </div>
  );
}
class Son extends React.Component{
  constructor(){
    super();
    this.state = {
    n: 0,
    m: 0
    };
  }
  addN(){
    this.setState({n: this.state.n + 1});
  }
  addM(){
    this.setState({n: this.state.n + 1});
  }
  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>
    );
  }
}
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);

...或Object.assign复制属性

类组件的setState会自动合并第一层属性,不会合并第二层属性

function App() {
  return (
    <div className="App">
      爸爸
      <Son />
    </div>
  );
}
class Son 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: {
        name: "jack"                            // age 被置空
      }
    });
  }
  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>
        <hr />
        <div>user.name: {this.state.user.name}</div>
        <div>user.age: {this.state.user.age}</div>
        <button onClick={() => this.changeUser()}>change user</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);

函数组件里有n和m

用对象当数据时,setState不会自动合并,使用...或Object.assign复制属性

function App() {
  return (
    <div className="App">
      爸爸
      <Son />
    </div>
  );
}
class Son 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 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>
    );
  }
}
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>
  );
};
const rootElement = document.getElementById("root");
ReactDOM.render(<App />, rootElement);

事件绑定

类组件的事件绑定
class Son extends React.Component{
  addN = () => this.setState({n: this.state.n + 1});
  render(){
    return <button onClick={this.addN}>n+1</button>
   }
}

详细资料点击:React 函数组件与class 组件