React类组件和函数组件区别对比使用

如果觉得还有点用,请您给我一个赞!您的赞是我坚持下去的动力

环境:react 16.12.0

这里将会用类和函数组件2种方法实现同一目标来对比他们的区别

目录

  • 子组件向父组件通信的方法(如何调用父组件方法)
  • 父组件向子组件通信的方法(如何调用子组件方法)
  • setState对象成员合并问题
  • state变化

子组件向父组件通信,调用父组件方法

案例:
我们实现一个子组件Input,带有一个输入框和一个提交按钮
再实现一个父组件Form,希望当Input组件内用户输入完毕点提交后,父组件能获得到通知并拿到Input的值

  • 函数式组件实现方式
    函数式子组件 Input.js
//写一个Input组件,作为子组件用
function Input(props){
  //用于存放输入的值
  const [state, setState] = useState({inputValue:''});

  function onClick(){
    //这里就是父组件传递给子组件的方法,子组件可以通过props直接调用
    props.onSubmit(state.inputValue);
  }

  function onInput(v){
    setState({inputValue:v.target.value});
  }

  return (
    
); } export default Input;

函数式父组件 Form.js

import React from 'react';
import Input from './Input.js';

function Form(props){
  //提供给子组件使用
  function onSubmit(value){
    console.log(`子组件提交啦,用户输入的是:${value}`)
  }

  return (
    
); }
  • 类式组件实现方式
    类式子组件 Input.js
class Input_Class extends React.Component{

  constructor(props){
    super(props);
    this.state = {
      inputValue:''
    }
  }

  onClick=()=>{
    this.props.onSubmit(this.state.inputValue);
  }

  onInput=(v)=>{
    this.setState({inputValue:v.target.value});
  }

  render(){
    return (
      
); } } export default Input_Class;

类式父组件 Form.js


class Form_Class extends React.Component{
  constructor(props){
    super(props);
    this.state = {}
  }

  onSubmit =(value)=>{
    console.log(`子组件提交啦,用户输入的是:${value}`)
  }

  render(){
    return (
      
); } }

父组件向子组件通信,调用子组件内的方法

案例:
我们实现一个Input组件作为子组件
实现一个Form组件作为父组件
父组件内通过按钮点击后需要调用子组件Input内的clear()方法来清空输入框

实现方法如下:

  • 函数式组件实现方式
    子组件 Input.js
import React, { useState,useImperativeHandle,forwardRef }  from 'react';

//写一个Input组件,作为子组件用
function Input(props,ref){
  //用于存放输入的值
  const [state, setState] = useState({inputValue:''});

  //用于清除输入框里的内容
  function clear(){
    setState({inputValue:''});
  }

  //这里就是用来暴露给ref.current下面的东西
  useImperativeHandle(ref, () => ({
    //我们暴露给ref.current.clear()这么一个方法,用来调用内部的clear()
    clear: () => {
      clear();//这里调用内部的clear,当然你可以直接将clear的实现体写在这里
    }
  }));

  return (
    
); } Input= forwardRef(Input); export default Input;

父组件 Form.js

import React, { useState,useRef } from 'react';
import Input from './Input.js';

function Form(props){
  const refInput=  useRef();//生成一个ref一会儿用来绑定Input子组件
  function onClick(){
    //通过ref.current可以获取子组件内暴露给父组件的所有对象
    refInput.current.clear();
  }

  return (
    
); }
  • 类式组件实现方式
    子组件 Input.js -- 如果子组件是类式组件,则不需要做任何特殊处理
class Input_Class extends React.Component{

  constructor(props){
    super(props);
    this.state = {
      inputValue:''
    }
  }

  onClick=()=>{
    this.props.onSubmit(this.state.inputValue);
  }

  clear=()=>{
    this.setState({inputValue:''});
  }
  onInput=(v)=>{
    this.setState({inputValue:v.target.value});
  }
  render(){
    return (
      
); } }

父组件 Form.js

需要注意在antd2-3下,对于被Form.create()的组件,需要通过this.ref=ref} /> 来获取到真实ref


class Sortable_Class extends React.Component{
  constructor(props){
    super(props);
    this.state = {}
    this.refInput = React.createRef();
  }

  btnSubmit=(value)=>{
    console.log(`input value:${value}`);
  }
  btnClearInput=()=>{
    this.refInput.current.clear();
  }

  render(){
    return (
      
); } }

setState

在函数式组件中,setState的时候不会合并对象成员,我们需要手动使用{...param,...newParam}等方式进行合并

函数式组件


function Counter() {
  const [param,setParam] = useState({a:1,b:2})

  useEffect(() => {
    setParam({b:3});// 解决方案: setParam( {...param,...{b:3}} )
  },[]); 
  
  return (
    <>
      param:{JSON.stringify(param)}
    
  );
}

渲染输出param:{b:3}

类式组件

class CounterClass extends React.Component{
  constructor(props){
    super(props);
    this.state = {
      a:1,
      b:2,
    }
  }
    
  componentDidMount() {
    this.setState({b:3});
  }

  render(){
    return (
      <>
        param:{JSON.stringify(this.state)}
      
    );
  }
}

渲染输出param:{a:1,b:3}

但是如果遇到多层结构对象的时候,由于setState只做浅拷贝所以会出现问题
 this.state = {
      objList: {
        objA: {
          list: [],
          selected: 0,
        },
        objB: {
          list: [],
          selected: 1,
        },
      }
    }

var obj = {
      objList:{
        objB:{
          list:[5,2],
          selected:11
        }
      }
    };
this.setState(obj);

这里setState后的结果是 {"objList":{"objB":{"list":[5,2],"selected":11}}} ,objA没了

解决方案是在多层的时候将外层的对象进行展开合并

 var obj = {
      objList:{
        ...this.state.objList, //展开父级
        objB:{
          list:[5,2],
          selected:11
        }
      }
    };

    this.setState(obj);

state变化

当我们点击一个按钮时触发一个延时alert,时间到了打印count的值

函数式组件

function Counter() {
  const [count,setCount] = useState(0);
  //const countRef = useRef();
  //useEffect(()=>{countRef.current=count});

  function handleClick(count) {
    setTimeout(() => {
      alert('You clicked: ' + count);
      //alert('You clicked:' + countRef.current);
    }, 3000);
  }

  return (
      

{count}

); }

加入点击Click Show Counter后点击3下Click me,则输出结果为: 0
这是由于handleClick调用时,count以当时的值存在闭包内,所以延时后打印的值任是0,解决方法为使用 useRef,代码片段中注释的部分打开

类式组件

export class CounterClass extends React.Component{
  constructor(props){
    super(props);
    this.state = {count:0}
  }

  handleClick=()=>{
    setTimeout(() => {
      alert('You clicked: ' + this.state.count);
    }, 3000);
  }

  render(){
    return (
      <>
        

{this.state.count}

); }

加入点击Click Show Counter后点击3下Click me,则输出结果为: 3

如果觉得还有点用,请您给我一个赞!您的赞是我坚持下去的动力

你可能感兴趣的:(React类组件和函数组件区别对比使用)