React教程(二):React组件基础

传送门:
React教程(一):React基础

一.组件概念

react官方解释:
React 允许你将标记、CSS 和 JavaScript 组合成自定义“组件”,即应用程序中可复用的 UI 元素。

个人理解:
组件是对一些UI和数据的封装,便于复用和维护。

    可以把页面中的所有组成部分都当成是组件,即可以把页面中一切元素都当成是组件(比如一个简单的button按钮)。组件可大可小,大的组件代码较多能够完成丰富的功能实现和展示,小的组件也许就是一个简单的标签而已。不同的组件有不同的功能,组件可以嵌套可以组合等等

    理解了这些概念之后,就会发现其实所有的页面都可以由多个组件构成,比如导航组件、商品展示组件、购物车组件等等。

放一张图更好的理解组件:

在这里插入图片描述

二.函数组件

React组件分为函数组件类组件,我们先来了解一下函数组件。

1.概念

使用JS的函数(或者箭头函数)创建的组件,叫做函数组件

2.定义与渲染

// 引入react核心包
import React from "react";
其他的时间
// 普通函数形式定义函数组件
function MyComponent() {
    
    
  return <div>我是一个函数组件</div>;
}
// 箭头函数形式定义函数组件
const MyComponent2 = () => {
    
    
  return <div>我是一个函数组件</div>;
};
function App() {
    
    
  return (
    <div>
      {
    
    /* 使用时可以双标签闭合 */}
      <MyComponent></MyComponent>
      {
    
    /* 也可以单标签闭合 */}
      <MyComponent />
      <MyComponent2 />
    </div>
  );
}
// 导出组件
export default App;

3. 约定说明

  1. 组件的名称必须首字母大写,react内部会根据这个判断是组件还是HTML标签。
  2. 函数组件必须要有返回值,表示该组件的UI结构,如果不需要渲染任何内容则返回null。
  3. 组件就像HTMl标签一样可以被渲染到HTMl页面中,组件表示的是一段结构内容,对于函数组件来说,渲染的内容是函数的返回值对应的内容

三. 类组件

1. 概念

使用ES6的class创建的组件,叫做类组件。

2. 定义与渲染

// 引入react核心包
import React from "react";

// 定义类组件
// 表示继承React组件类(父类)
class Hello extends React.Component {
    
    
  // render函数用于渲染ui
  render() {
    
    
    return <div>我是类组件</div>; /* 返回值 */
  }
}
function App() {
    
    
  return (
    <div>
      <Hello />
    </div>
  );
}

// 导出组件
export default App;

3.约定说明

  1. 类名也必须以大写字母开头
  2. 类组件应该是继承React.Component父类,从而使用父类中提供的方法或属性。
  3. 类组件必须提供render方法,render方法必须有返回值,表述组件的UI结构

四. 组件事件绑定

1. 如何绑定事件

  • 语法
    on + 事件名 = (事件处理函数),例如绑定一个点击事件:
<div onClick={
    
    ()=>{
    
    }}>点我</div>
  • 注意点
    react 事件采用驼峰命名法,比如onClick、onMouseEnter、onFocus
  • 样例
// 引入react核心包
import React from "react";

// 函数组件
const HelloWorld = () => {
    
    
  // 定义事件回调函数
  const dealClick = () => {
    
    
    alert("函数组件,你好");
  };
  return <div onClick={
    
    dealClick}>点我弹出你好</div>;
};
// 类组件
class Hello extends React.Component {
    
    
  // 定义事件回调函数(标准写法,避免this组件问题),回调函数中的this会指向当前的实例对象
  dealClick = () => {
    
    
    alert("类组件,你好");
  };
  render() {
    
    
    // this会指向当前的实例对象(表示调用实例对象中的方法)
    return <div onClick={
    
    this.dealClick}>点我弹出你好</div>; /* 返回值 */
  }
}
function App() {
    
    
  return (
    <div>
      <HelloWorld />
      <Hello />
    </div>
  );
}

export default App;
  • 注意点:
    在类组件中,请注意定义事件函数的标准写法,避免this指向问题:
  dealClick = () => {
    
    
    alert("类组件,你好");
  };

2. 获取事件对象

  • 通过事件处理程序的参数获取事件对象
// 引入react核心包
import React from "react";

// 函数组件
const HelloWorld = () => {
    
    
  // 定义事件回调函数
  const dealClick = (e) => {
    
    
    // 阻止默认事件(此时a标签被点击则不会触发跳转事件)
    e.preventDefault();
    console.log("e事件对象", e);
  };
  return (
    <a onClick={
    
    dealClick} href="www.baidu.com">
      点我打印事件对象
    </a>
  );
};

function App() {
    
    
  return (
    <div>
      <HelloWorld />
    </div>
  );
}

export default App;

在这里插入图片描述

扫描二维码关注公众号,回复: 14663226 查看本文章

可见事件对象e被打印到了控制台。

  • 事件绑定传递自定义参数

利用箭头函数的形式去传递额外参数

const HelloWorld = () => {
    
    
  // 定义事件回调函数
  const dealClick = (msg) => {
    
    
    console.log("msg", msg);
  };
  return (
    <div
      onClick={
    
    () => {
    
    
        dealClick("icy is godlike");
      }}
    >
      点我打印msg
    </div>
  );
};
function App() {
    
    
  return (
    <div>
      <HelloWorld />
    </div>
  );
}

export default App;

执行的结果:
在这里插入图片描述

  • 同时传递额外参数和获取事件对象e
    需要将事件对象e作为形参传递。
const HelloWorld = () => {
    
    
  // 定义事件回调函数
  const dealClick = (e, msg) => {
    
    
    // 此处接收时间对象e和额外参数msg
    console.log("e事件对象", e);
    console.log("msg", msg);
  };
  return (
    <div
      onClick={
    
    (e) => {
    
    
        // 需要在此处将事件对象e作为形参传入
        dealClick(e, "icy is godlike");
      }}
    >
      点我打印msg和事件对象e
    </div>
  );
};

打印的结果:
在这里插入图片描述
写法小结:

  1. 不需要传递参数 onClick={函数名} ,在该函数中可以直接获取事件对象e
  2. 需要传递参数 onClick = { ( )=>{ 函数名(参数) } } // 利用箭头函数
  3. 需要事件对象e又需要传递参数 ,把e作为参数传入
    onClick = { ( ) => { 函数名(e, 其他参数) } }

五. 组件状态

背景:

在react hook 出现之前,函数组件没有自己的状态,所以我们先通过类组件来讲解组件状态。

流程: 初始化状态 -> 读取状态-> 修改状态 -> 影响视图

1.初始化状态

  • 通过类的实例属性state来初始化状态
  • state的值是一个对象结构,表示一个组件可以有多个数据状态
class HelloWorld extends React.Component {
    
    
  state = {
    
    
    count: 81,
  };
  render() {
    
    
    return <div></div>;
  }
}

2.读取状态

通过this.state 来读取状态

class HelloWorld extends React.Component {
    
    
  state = {
    
    
    count: 81,
    name: "icy",
  };
  render() {
    
    
    return (
      <div>
        {
    
    this.state.name}的体重:{
    
    this.state.count}
        <div>
          <button>点我减少体重</button>
        </div>
      </div>
    );
  }
}

渲染出来的效果:

3.修改状态

通过this.setState() 方法来修改
注意: 不可以直接赋值修改

class HelloWorld extends React.Component {
    
    
  // 初始化状态
  state = {
    
    
    weight: 81,
    name: "icy",
  };
  // 定义回调函数修改状态
  loseWeight = () => {
    
    
    this.setState({
    
    
      // 将state中的值解构出来
      ...this.state,
      // 修改count的值为原来的值减1
      weight: this.state.weight - 1,
    });
    // 打印一下结果
    console.log("icy的体重:", this.state.weight);
  };
  render() {
    
    
    return (
      <div>
        {
    
    this.state.name}的体重:{
    
    this.state.weight}
        <div>
          {
    
    /* 这里别忘记了绑定事件 */}
          <button onClick={
    
    this.loseWeight}>点我减少体重</button>
        </div>
      </div>
    );
  }
}

执行的结果:
在这里插入图片描述

注意事项:

  1. 修改基本数据类型
this.setState({
     
     name:'新的值'})
  1. 修改复杂数据类型(对象,数组),可以通过扩展运算符解构(浅拷贝)
this.setState(list:[...this.state.lsit,'新的一项']

3.注意如果state中状态过多,也可以通过解构赋值来修改

   this.setState({
     
     
	// 将state中的值解构出来
	...this.state,
 	// 修改count的值为原来的值减1
 	weight: this.state.weight - 1,
   });

六. this指向问题说明

必须谨慎对待JSX回调函数中的this,在javascript中,class方法默认不会绑定this,如果忘记绑定 this.handleClick,并把它传入onClick,则当你调用这个函数的时候,this的值将会为undefined。

代码说明:

1.错误的使用方法

class HelloWorld extends React.Component {
    
    
  // 初始化状态
  state = {
    
    
    weight: 81,
    name: "icy",
  };
  // 定义回调函数修改状态
  loseWeight() {
    
    
    this.setState({
    
    
      // 将state中的值解构出来
      ...this.state,
      // 修改count的值为原来的值减1
      weight: this.state.weight - 1,
    });
    // 打印一下结果
    console.log("icy的体重:", this.state.weight);
  }
  render() {
    
    
    return (
      <div>
        {
    
    this.state.name}的体重:{
    
    this.state.weight}
        <div>
          <button onClick={
    
    this.loseWeight}>点我减少体重</button>
        </div>
      </div>
    );
  }
}

此时会报错,因为this为undefined。
在这里插入图片描述

2. 正确的写法(1)

在类的构造函数constructor中手动改变回调函数的this指向

class HelloWorld extends React.Component {
    
    
  // 初始化状态
  state = {
    
    
    weight: 81,
    name: "icy",
  };
  constructor() {
    
    
    // super()固定写法用来继承父类的方法或属性
    super();
    // 使用bind函数改变loseWeight的this指向为当前类的实例对象
    // 即在组件初始化时就修正了该方法的this指向
    this.loseWeight = this.loseWeight.bind(this);
  }
  // 定义回调函数修改状态
  loseWeight() {
    
    
    this.setState({
    
    
      // 将state中的值解构出来
      ...this.state,
      // 修改count的值为原来的值减1
      weight: this.state.weight - 1,
    });
    // 打印一下结果
    console.log("icy的体重:", this.state.weight);
  }

  render() {
    
    
    return (
      <div>
        {
    
    this.state.name}的体重:{
    
    this.state.weight}
        <div>
          <button onClick={
    
    this.loseWeight}>点我减少体重</button>
        </div>
      </div>
    );
  }
}
export default App;

3.正确的写法(2)

给onClick绑定事件时,使用箭头函数的写法

class HelloWorld extends React.Component {
    
    
  // 初始化状态
  state = {
    
    
    weight: 81,
    name: "icy",
  };
  // 定义回调函数修改状态
  loseWeight() {
    
    
    this.setState({
    
    
      // 将state中的值解构出来
      ...this.state,
      // 修改count的值为原来的值减1
      weight: this.state.weight - 1,
    });
    // 打印一下结果
    console.log("icy的体重:", this.state.weight);
  }

  render() {
    
    
    return (
      <div>
        {
    
    this.state.name}的体重:{
    
    this.state.weight}
        <div>
          <button
            /* 此处使用箭头函数写法,箭头函数没有自己的this,会向作用域链上级查找 */
            onClick={
    
    () => {
    
    
              this.loseWeight();
            }}
          >
            点我减少体重
          </button>
        </div>
      </div>
    );
  }
}

4.正确的写法(3)最推荐的写法

在定义回调函数时,直接使用箭头函数的写法。

class HelloWorld extends React.Component {
    
    
  // 初始化状态
  state = {
    
    
    weight: 81,
    name: "icy",
  };
  // 此处直接写成箭头函数的形式,从而引用作用域链上级的this
  loseWeight = () => {
    
    
    this.setState({
    
    
      // 将state中的值解构出来
      ...this.state,
      // 修改count的值为原来的值减1
      weight: this.state.weight - 1,
    });
    // 打印一下结果
    console.log("icy的体重:", this.state.weight);
  };

  render() {
    
    
    return (
      <div>
        {
    
    this.state.name}的体重:{
    
    this.state.weight}
        <div>
          {
    
    /* 此处保持原样 */}
          <button onClick={
    
    this.loseWeight}>点我减少体重</button>
        </div>
      </div>
    );
  }
}

七.React的状态不可变说明

**概念:**不要直接修改状态的值,而是基于当前状态创建新的状态值。

1.错误的做法:直接修改state的值

  // 初始化状态
  state = {
    
    
    weight: 81,
    list: [1, 2, 3],
    person: {
    
    
      name: "icy",
      age: 23,
    },
  };

  updateState = () => {
    
    
    // 直接修改简单类型Number(错误,千万别这么干,达咩)
    this.state.weight++;
    ++this.state.weight;
    this.state.weight += 1;
    this.state.weight = 70;

    // 直接修改数组 千万别这么干,达咩!!
    this.state.list.push(2222);
    this.state.list.slice(1, 2);

    // 直接修改对象 千万别这么干,达咩达咩哟!!
    this.state.person.name = "icy大魔王";
  };

vscode也会给出warning,让你别这么干。
在这里插入图片描述

2.正确的做法:基于当前状态创建新的值

this.setState({
    
    
      count: this.state.count + 1,
      list: [...this.state.list, 4],
      person: {
    
    
        ...this.state.person,
        // 解构然后覆盖原来的name,实际工作中中常用写法
        name: "icy大魔王",
      },
    });

八.处理表单

使用React处理表单元素,一般有两种方式:

1.受控组件(推荐使用)
2.非受控组件

1.受控表单组件

什么是受控组件?
受控组件是可以被react的状态控制的组件
React组件的状态在state中,input表单元素也有自己的状态在value中,React将state与表单元素的值(value)绑定到一起,由state的值来控制表单元素的值,从而保证单一数据源特性。

实现步骤:
以获取文本框的值为例,受控组件使用步骤如下:

  1. 在组件的state中声明一个组件的状态数据
  2. 将状态数据设置为input标签元素的value属性的值
  3. 为input添加onChange事件
  4. 在事件处理函数中,通过事件对象e获取到当前文本框的值(即用户输入的值)
  5. 调用setState方法,将文本框的值作为state状态的最新值。

代码演示:

class HelloWorld extends React.Component {
    
    
  // 初始化状态
  state = {
    
    
    // 用来控制input输入框的value状态,初始为空
    text: "",
  };
  // 事件处理函数
  changeText = (e) => {
    
    
    // 使用e事件对象获取用户输入的值,并打印出来看看对不对
    // 此处也可以自行打印e对象,看看value是如何拿到的
    console.log("用户输入的值:", e.target.value);
    // 使用setStae改变state的状态为用户输入的值
    this.setState({
    
    
      text: e.target.value,
    });
  };
  render() {
    
    
    return (
      <div>
        {
    
    /* 此处value绑定state中的text状态 同时绑定表单的onChange事件*/}
        <input value={
    
    this.state.text} onChange={
    
    this.changeText} />
      </div>
    );
  }
}

控制台效果:
在这里插入图片描述

如果大家还学过vue,vue中有个v-model指令可以双向绑定表单数据,以上也是双向绑定的原理。

2.非受控组件

什么是非受控组件?
非受控组件是通过手动操作dom的方式获取文本框的值,文本框的状态不受react组件中的state控制,直接通过原生dom获取输入框的值。

实现步骤:

  1. 导入createRef函数
  2. 调用createRef函数,创建一个ref对象,存储到名为xxxx(自定义名字如如:msgRef)的实例属性中
  3. 为input添加属性,值为msgRef(上面自定义的名字)
  4. 在按钮的事件处理函数中,通过msgRef.current(固定写法)即可拿到input对应的dom元素,而其中msgRef.current.value(固定写法)拿到的则是文本框的值。

代码演示:

class HelloWorld extends React.Component {
    
    
  // 使用createRef创建一个可以存放dom对象的容器
  msgRef = createRef();

  changeMsg = () => {
    
    
    // 获取输入框用户输入的值 this.msgRef.current.value
    console.log("value:", this.msgRef.current.value);
    //
  };
  render() {
    
    
    return (
      <div>
        {
    
    /* 给表单绑定一个ref属性 */}
        <input ref={
    
    this.msgRef} onChange={
    
    this.changeMsg} />
      </div>
    );
  }
}

控制台效果:
在这里插入图片描述

猜你喜欢

转载自blog.csdn.net/qq_43682422/article/details/129799251