Bootstrap

React 基础篇(二)

💻 React 基础篇(二)🏠专栏:React
👀个人主页:繁星学编程🍁
🧑个人简介:一个不断提高自我的平凡人🚀
🔊分享方向:目前主攻前端,其他知识也会阶段性分享🍀
👊格言:☀️没有走不通的路,只有不敢走的人!☀️
👉让我们一起进步,一起成为更好的自己!!!🎁

React 基础知识篇(二)

style 样式

类组件和函数组件添加样式的方式是完全一样的

注意点

  1. 添加样式的时候,要使用两个大括号{{}},外面是jsx的括号(表示将{}中的值变为js表达式),里面是对象的括号
return <div style={{color:'red'}}>style</div>
  1. 如果有连字符的,要写成小驼峰的语法
return <div style={{color:'red', fontSize:"20px"}}>style</div>
  1. 如果说单位是px的,可以省略单位,并且写成数字

:并不是所有样式属性的px单位都可以省略(如lineHeight原本就不可以省略,省略后代表的是字体小的倍数)

return <div style={{color:'red',fontSize:20,height:'50px',lineHeight:2}}>style</div>
}

注释

jsx中的注释会被{}包裹,因为注释也被默认当作是js表达式

class About extends React.Component {
    render() {
        return <>
        {/* <div>nihao</div> */}
        </>
    }
}

className 类名

通过添加className类名修改样式

.test1 {
  color: red;
}
.test2 {
  background-color: aqua;
}
import React from 'react'
// 1. 引入css文件
import './class.css'
const testClass = ()=>{
    return <>
    <div className='test1 test2'>通过class修改样式</div>
    </>
}
export default testClass

:在标签中不能用class属性要用className,因为jsx中使用了类,防止起冲突。

还有一种标签上的属性名在jsx中发生了改变(label标签上的for属性改为htmlFor,防止与for循环起冲突)

<label htmlFor="fos">年龄</label>
<input type="text" id='fos' placeholder='请输入你的年龄'/>

不同的条件添加不同的类名

(1) 使用三目运算

class.css文件

.red {
  color: red;
}

.green {
  color: green;
}

App.jsx根组件

import React from 'react'
// 1. 引入css文件
import './class.css'
// 2. 设置变量
const red = false;
const App = () => {
  return (
    <>
      <h2>不同条件添加不同样式</h2>
      {/* 通常情况下,可以使用三目来解决问题 */}
      <p className={red ? "red" : "green"}>hello react!!</p>
    </>
  );
};
export default App

(2) 使用第三方包实现

classnames

官网
classnames

安装

npm install classnames
yarn add classnames
import { Component } from 'react';
import classNames from 'classnames/bind';
import styles from './class.css';

let cx = classNames.bind(styles);
let show = true;

class changeColor extends Component {
  render () {
    let className = cx({
        colorRed: show,
        colorGreen: show,
    });
    return <div className={className}>通过第三包classNames修改样式</div>
  }
};
export default changeColor
styled-components

官网
styled-components
安装

npm install styled-components
yarn add styled-components

使用

import React from 'react'
import styled  from 'styled-components';
// 方式一:
const Title = styled.h  1`
  font-size: 1.2em;
  text-align: center;
  color: palevioletred;
`;
// 方式二:进行函数传参
const Title1 = styled.h2`
    /* 还可以使用自定义属性进行props的传参 */
    font-size: ${(props) => props.size + "px"};
    color: ${(props) => props.color};
`;

const testClass = ()=>{
    return <>
    <Title className={Title}>通过class修改样式</Title>
    <Title1 size='30'>h2</Title1>
    <Title1 color='green'>h2-2</Title1>
    </>
}
export default testClass

安装vscode中的插件:vscode-styled-components(可以在styled.xx后的字符串中进行标签提示)

props

(1) 使用

props是父组件传递给子组件的自定义属性
特点

  • props 具有响应式
  • 遵循 单向数据流
  • 子组件 不能直接修改props的值

注:

  • 类组件通过this.props获取父组件传递的值
  • 函数组件通过函数参数props获取父组件传递的值
import React, { Component } from 'react'
// 类组件通过this.props获取父组件传递的值
class Child1 extends Component {
    render() {
        const { name, age } = this.props
        return (
            <div>child1 - {name} - {age}</div>
        );
    }
}
// 函数组件通过函数参数props获取父组件传递的值
const Child2 = ({ count }) => {
    return <>
        <div>child2 - {count}</div>
    </>
}

class App extends Component {
     state = { 
        name:'zs'
      }
    render() {
        return <>
            <div>props传参</div>
            <Child1 age={10} name={this.state.name} />
            <Child2 count={100} />
        </>
    }
    componentDidMount() {
        setTimeout(() => {
            this.setState({
                name: "lisi",
            });
        }, 2000);
    }
}

export default App

(2) 默认值

import React, { Component } from 'react'

class Child1 extends Component {
    // 类组件设置默认值的方式一
    static defaultProps = {
        name:'ww',
        age: 18,
        sex:'男'
    }
    render() {
        const { name, age, sex } = this.props
        return (
            <div>child1 - {name} - {age} - {sex}</div>
        );
    }
}
// 类组件设置默认值的方式二
// Child1.defaultProps = {
//     name:'ww',
//     age: 18,
//     sex:'男'
// }

const Child2 = ({ count, sex }) => {
    return <>
        <div>child2 - {count} - {sex}</div>
    </>
}
// 函数组件设置默认值的方式
Child2.defaultProps = {
    count: 18,
    sex:'女'
}

class App extends Component {
     state = { 
        name:'zs'
      }
    render() {
        return <>
            <div>props传参</div>
            <Child1 name={this.state.name} age={"19"}/>
            <Child2 count={100} />
        </>
    }
}

export default App

(3) 校验props接收传递值的数据类型

使用第三方包:prop-types 官网

prop-types

安装

npm install --save prop-types
yarn add prop-types --save 

常用的支持检测的数据类型

  optionalArray: PropTypes.array,
  optionalBigInt: PropTypes.bigint,
  optionalBool: PropTypes.bool,
  optionalFunc: PropTypes.func,
  optionalNumber: PropTypes.number,
  optionalObject: PropTypes.object,
  optionalString: PropTypes.string,
  optionalSymbol: PropTypes.symbol,
  optionalEnum: PropTypes.oneOf(['News', 'Photos']),
  optionalUnion: PropTypes.oneOfType([
    PropTypes.string,
    PropTypes.number,
    PropTypes.instanceOf(Message)
  ]),
  requiredFunc: PropTypes.func.isRequired,
  requiredAny: PropTypes.any.isRequired,

使用

import React, { Component } from 'react'
// 1.直接解构
import {string,number,oneOfType} from 'prop-types';

class Child1 extends Component {
    // 类组件检测数据类型的方式一
    static propTypes = {
        name:string,
        age:oneOfType([
            string,
            number
          ]),
    }
    render() {
        const { name, age,sex } = this.props
        return (
            <div>child1 - {name} - {age} - {sex}</div>
        );
    }
}
// 类组件检测数据类型的方式二
// Child1.propTypes = {
//     name:string,
//     age:number
// }

const Child2 = ({ count,sex }) => {
    return <>
        <div>child2 - {count} - {sex}</div>
    </>
}
// 函数组件检测数据类型的方式
Child2.propTypes = {
    count:number
}

class App extends Component {
     state = { 
        name:'zs'
      }
    render() {
        return <>
            <div>props传参</div>
            <Child1 name={this.state.name} age={"19"}/>
            <Child2 count={100} />
        </>
    }
}

export default App

条件渲染

(一) 三目运算符

(1) 变量三目

通过state中的变量控制标签的显示与隐藏(相当于Vue中的v-if)

class TestState extends Component {
    constructor(){
        super()
        this.state = {
            show: false
        }
    }
    render() {
        const {show} = this.state;
        return (
            <>
            {/* 变量三目运算 */}
            {show ? <p>显示1</p>:null}
            </>
        );
    }
}
(2) style三目

通过state中的变量控制标签样式切换来控制标签的显示与隐藏(相当于Vue中的v-show)

class TestState extends Component {
    constructor(){
        super()
        this.state = {
            show: false
        }
    }
    render() {
        const {show} = this.state;
        return (
            <>
            {/* style三目运算 */}
			<p style={{display : show?"block":"none"}}>显示3</p>
            </>
        );
    }
}
(3) class三目

通过state中的变量控制标签不同类名的样式切换来控制标签的显示与隐藏(相当于Vue中的v-show)

class TestState extends Component {
    constructor(){
        super()
        this.state = {
            show: false
        }
    }
    render() {
        const {show} = this.state;
        return (
            <>
            {/* class三目运算(通过切换类名实现) */}
            <p className={show?'show':'noshow'}>显示4</p>
            </>
        );
    }
}
// 样式文件中:
.show {
  display: block;
}
.noshow {
  display: none;
}

(二) 短路运算符

通过短路运算符(&&)来控制标签的显示与隐藏(Vue中的v-if)

class TestState extends Component {
    constructor(){
        super()
        this.state = {
            show: false
        }
    }
    render() {
        const {show} = this.state;
        return (
            <>
            {/* 短路运算 */} 
            {show && <p>显示2</p>}
            </>
        );
    }
}

循环渲染

通过数组的map方法的映射来进行标签内容的循环渲染

class TestState extends Component {
    constructor(){
        super()
        this.state = {
            list: [1,2,3,4,5]
        }
    }
    render() {
        const {show} = this.state;
        return (
            <>
            {/* 循环渲染 */}
            <ul>
                {list.map(item =><li key={item}>{item}</li>)}
            </ul>
            </>
        );
    }
}

插槽

(1) 使用

父组件(在父组件的子组件标签内容中直接写要传递的内容(也可以是标签))

import React, { Component } from 'react'

class App extends Component {
  render() {
      return <>
          <div>React 插槽</div>
          <Child1>
              <h1>h1</h1>
          </Child1>
          <Child2>
              <span>span1</span>
          </Child2>
      </>
  }
}
export default App

类组件接收(使用this.props中的children)

import React, { Component } from 'react'
// 类组件使用this.props中的children进行接收父组件的子组件上的标签
class Child1 extends Component {
  render() {
      return (
          <>
              <div>子类组件 - {this.props.children}</div>
          </>
      );
  }
}

函数组件接收(使用函数形参children)

import React from 'react'
// 函数组件使用函数形参children进行接收父组件的子组件上的标签
const Child2 = (props) => {
  return <>
      <div>子函数组件 - {props.children}</div>
  </>
}

(2) 具名插槽

数组形式
可以在父组件传递多个内容时使用数组形式,子组件中使用数组的索引让其使用在不同的位置

  • 父组件
import React, { Component } from 'react'

class App extends Component {
  render() {
      return <>
          <div>React 插槽</div>
          <Child1>
              <h1>h1</h1>
              <h2>h2</h2>
          </Child1>
          <Child2>
              <span>span1</span>
              <span>span2</span>
          </Child2>
      </>
  }
}
export default App
  • 子类组件
class Child1 extends Component {
  render() {
      return (
          <>
              <div>{this.props.children[0]} - 子类组件 - {this.props.children[1]}</div>
          </>
      );
  }
}
  • 子函数组件
const Child2 = (props) => {
  return <>
      <div>{props.children[0]} - 子函数组件 - {props.children[1]}</div>
  </>
}

对象形式
可以在父组件传递多个内容时使用对象形式,子组件中使用数组的索引让其使用在不同的位置

  • 父组件
import React, { Component } from 'react'

class App extends Component {
  render() {
      return <>
          <div>React 插槽</div>
          <Child1>
              {{
                  h1: <h1>h1</h1>,
                  h2: <h2>h2</h2>
              }}
          </Child1>
          <Child2>
              {{
                  span1: <span>children1</span>,
                  span2: <span>children2</span>
              }}
          </Child2>
      </>
  }
}
export default App
  • 子类组件
class Child1 extends Component {
  render() {
      return (
          <>
              <div>{this.props.children.h1} - 子函数组件 - {this.props.children.h2}</div>
          </>
      );
  }
}
  • 子函数组件
const Child2 = (props) => {
  return <>
      <div>{props.children.span1} - 子函数组件 - {props.children.span2}</div>
  </>
}

state

相当于Vue中的data(响应式数据) 只有类组件可以定义自己的状态(state)

定义state的两种方式

使用构造函数的构造器进行定义

class TestState extends Component {
  // constructor当类实例化的时候被调用
  constructor(){
      // 在constructor里面需要先调用super方式才能使用this
      super()
      this.state = {
          name:'zs'
      }
  }
  render() {
      // 方式一:
      // return (
      //     <>
      //     <div>count:{this.state.count}</div>
      //     </>
      // );
      // 方式二:解构
      const {count,name} = this.state;
      return (
          <>
          <div>count:{count}</div>
          <div>name:{name}</div>
          </>
      );
  }
}

ES7 属性初始化器

class TestState extends Component {
  state = { 
      count:10
  };
  render() {
      const {count,name} = this.state;
      return (
          <>
          <div>count:{count}</div>
          <div>name:{name}</div>
           {/* 添加属性的时候,如果使用了变量({}),不能用引号 */}
          <p title={this.state.title}>react</p>
          </>
      );
  }
}

jsx中添加属性:(如果使用了变量({}),不能用引号)

<p title={this.state.title}>react</p>

setState

不能直接修改state的值,直接修改没有响应式

this.state.count = 10

setState里面有两个参数

  • 第一个参数:对象或函数
  • 第二个参数:回调函数

第一个参数可以是一个对象,这个对象会和state对象合并

this.setState({
    count:this.state.count+1,
    name:'ls'
})

当连续修改同一个变量的值,由于setState是异步的会同时执行,所以使用对象形式无法实现+1+1

this.setState({
    count:this.state.count+1,
    name:'ls'
})
this.setState({
    count:this.state.count+1,
    name:'ls'
})

第一个参数也可以是一个函数

如果一次的改动要基于前一次的值,用函数的写法(场景:上拉加载

this.setState((prevState)=>{
    return{ count:prevState.count+1}
})
this.setState((prevState)=>{
    return{ count:prevState.count+1}
})

如果想要省略大括号和return,需要加小括号

this.setState((prevState)=>({
    count:prevState.count+1
}))
// 此时打印,输出的是修改前的值
console.log(this.state.count);// 修改前的值

第二个参数:可选的回调函数,会在数据改变之后调用

this.setState({
  count:this.state.count+1,
  name:'ls'
},()=>{
  console.log(this.state.count);
})

实现:在数组list中添加一项:4

this.setState((prevState)=>({
    // 方式一:利用数组的concat方法
    // list:prevState.list.concat(4)
    // 方式二:利用拓展运算符
    list:[...prevState.list,4]
}))
console.log(this.state.count);
// 仅限于复杂数据类型
// 方式三:直接修改list,然后在合并
this.state.list.push(4)
this.setState({})

  • 18版本之后所有的setState都是异步的
  • 18版本之前(定时器、原生js是同步的)、合成事件和生命周期是异步的

解析state中的标签

类似于Vue中的v-html

state = { 
    content:'<h1>v-html</h1>'
}
<div dangerouslySetInnerHTML={{__html:this.state.content}}></div>

事件的传参与this的指向

合成事件:事件名称要写成小驼峰(onClick)

<button onClick={this.handleClick}>btn</button>

this的指向

在类组件中直接写普通函数当事件处理函数的时候会出现this的指向不对的问题

改变this的指向的方式

直接内联地使用 this.xxx.bind(this)(不推荐)

bind函数会产生一个新函数,并且新函数会存在于内存中,会导致内存占用太高

<button onClick={this.handleClick.bind(this)}>btn</button> 

在constructor构造器里面使用 this.xxx = this.xxx.bind(this); (比较推荐,官网在用)

将bind产生的新函数存下来了,不会造成内存增加的问题

constructor() {
  super()
  this.handleClick = this.handleClick.bind(this)
}

使用箭头函数 (推荐)

箭头函数的this指向父级,既方便又不会增加内存

handleClick = () => {
  console.log(this.state.count);
}

事件传参

事件后面不要加括号,函数会自执行

<button onClick={this.handleClick()}>btn</button>

事件传参的方式

内联地使用 this.xxx.bind(this, 2)

bind可以改变this指向。会产生一个新函数,bind的第二个参数会作为新函数的第一个参数 bind函数是不会自动调用的

不推荐,会使得内存增加

class App extends Component {
  state = {
      count: 1
  }
  handleClick(n) {
      console.log(this.state.count, n);
  }
  render() {
      return (
          <>
          <div>事件</div>
          <button onClick={this.handleClick.bind(this, 2)}>btn</button>
          </>
      );
  }
}

内联地使用 onClick={() => this.xxx(2)}

不是特别推荐, 用的最多的方式 每一个箭头函数都会指向不同的内存,也会使内存增加

class App extends Component {
  state = {
      count: 1
  }
  handleClick = (n,e) => {
      console.log(this.state.count, n,e);
  }
  render() {
      return (
          <>
              <div>事件</div>
              <button onClick={(e) => this.handleClick(2,e)}>btn</button> 
          </>
      );
  }
}

使用柯里化函数

class App extends Component {
  state = {
      count: 1
  }
  handleClick = (n) => (e) => {
      console.log(this.state.count, n ,e);
  }
  render() {
      return (
          <>
          <div>事件</div>
          <button onClick={this.handleClick(2)}>btn</button>
      );
  }
}
fn = (n) => () => {
console.log(n);
};

理论上最推荐的方式

受控组件和非受控组件

表单输入绑定

它们都是特指表单控件

  • 受控组件让我们自己来控制的表单
  • 只要input有value属性和onChange事件,那么就是受控组件
import React, { Component } from 'react'

class App extends Component {
    state = {
        iptvalue: 'zs'
    }
    changeIpt = (e) => {
        this.setState(() => {
            return {
                iptvalue: e.target.value
            }
        })
    }
    render() {
        return (
            <>
            <h2>表单绑定</h2>
            <input type="text" value={this.state.iptvalue} onChange={this.changeIpt} />
            <p>{this.state.iptvalue}</p>
            </>
        );
    }
}

export default App;

非受控组件

使用createRef去创建内存,获取dom节点,使用变量指向该内存,然后绑定到input上
非受控组件的默认值要用defaultValue

<input type="text" defaultValue='非受控组件' />
import React, { Component, createRef } from 'react'

class App extends Component {
    constructor() {
        super()
        this.ipt = createRef()
    }
    state = {
        textValue: 'zs'
    }
    chageIpt = () => {
        console.log(this.ipt);
    }
    render() {
        return (
            <>
            <h1>非受控组件</h1>
            {/* 非受控组件的默认值要用defaultValue */}
            <input type="text" ref={this.ipt} defaultValue='非受控组件' />
            <button onClick={this.chageIpt}>btn</button>
            </>
        );
    }
}

export default App;

使用场景:文件上传框(只读,不能去修改信息)

状态提升

就是兄弟组件间传参
实现方式:先使用子传父,在利用父传子实现兄弟组件间的传参

  • 父传子:在父组件的子组件标签上添加自定义属性,然后:
    如果是类组件则通过this.props.属性名进行接收
    如果是函数组件则通过函数参数props.属性名进行接收
  • 子传父:在父组件的子组件标签上添加自定义事件,然后:
    如果是类组件则通过this.props.自定义事件名调用父组件中的事件,并进行参数传递
    如果是函数组件通过函数参数props.自定义事件名调用父组件中的事件,并进行参数传递

类组件中

传值的子组件1

import React, { Component } from "react";

class Child1 extends Component {
state = {
  count: 10,
};

render() {
  return <div>child1组件</div>;
}

componentDidMount() {
  this.props.onGetcount(this.state.count);
}
}

export default Child1;

父组件

import React, { Component } from "react";

import Child1 from "./Child1";
import Child2 from "./Child2";

class App extends Component {
state = {
  num: null,
};

fn = (c) => {
  // console.log("fn", c);
  this.setState({
    num: c,
  });
};

render() {
  return (
    <>
      <h2>状态提升</h2>
      <Child1 onGetcount={this.fn} />
      <Child2 num={this.state.num} />
    </>
  );
}
}

export default App;

接收的子组件2

import React, { Component } from "react";

class Child2 extends Component {
render() {
  return <div>child2组件 - {this.props.num}</div>;
}
}

export default Child2;

函数组件中

传值的子组件1

import React, { useState } from 'react'

const Child1 = (props) => {
  const [age] = useState(20)
  const handleClick = () => {
      props.onGetNum(age)
  }
  return (
      <>
          <h3>子组件1</h3>
          <span>C1: {age}</span>
          <button onClick={handleClick}>将age传递给父组件</button>
      </>
  );
}

export default Child1;

父组件

import React, { useState } from 'react'
import Child1 from './Child1';
import Child2 from './Child2';
const App = () => {
  const [count, setCount] = useState(0)
  const fn = (n) => {
      setCount(n)
  }
  return (
      <>
          <h3>父组件</h3>
          <span>Fcount: {count}</span>
          <Child1 onGetNum={fn} />
          <Child2 count={count} />
      </>
  );
}

export default App;

接收的子组件2

import React from 'react'

const Child2 = (props) => {
  return (
      <>
          <h3>子组件2</h3>
          <span>{props.count}</span>
      </>
  );
}

export default Child2;

结束语

希望对您有一点点帮助,如有错误欢迎小伙伴指正。
👍点赞:您的赞赏是我前进的动力!
⭐收藏:您的支持我是创作的源泉!
✍评论:您的建议是我改进的良药!
一起加油!!!💪💪💪

;