likes
comments
collection
share

一文让你快速上手React18,最直白知识点总结

作者站长头像
站长
· 阅读数 26

一文让你快速上手React18,最直白知识点总结

本文是个人自己学习React后的基础知识总结,是为了加深印象,以及后续项目中某些知识点记忆不清晰,能够快速查找到实用案例;希望对学习React的朋友们带来些许帮助,谢谢~

0、环境配置

1、使用 create-react-app 快速创建开发环境

npx create-react-app my-app --template typescript

2、个人自己搭建的开发环境

gitee下载地址:gitee.com/zhao-wencha…

github下载地址:github.com/773579084/R…

分支master: webpack5+typescript+react18+eslint+prettier+stylelintrc

分支mobx:master基础上增加 react-router6mobx6axios的基础封装

分支ant:mobx基础上增加 antecharts(按需引入、动态刷新)

如果基础开发环境能够给你带来帮助,麻烦给我个star吧!谢谢呀~

1、JSX插值表达式

    1-1、{/* 🔔// string number 正常显示 */}
    <h1>{'hello React'}</h1>
    <h1>{73}</h1>
​
    1-2、{/* null undefined  boolean  🔔不显示*/}
​
    1-3、{/* 💥对象不能直接放在插值表达式中,会报错 */}
    ex: const obj = {name:'zs'}
       {obj} => 报错
       {obj.name} => 🆗
      
    1-4、{/* 🔔 数组中的每项元素,都会当做一个dom节点渲染出来 */}
      //  数组
      const list = [<div>1</div>, <div>2</div>, <div>3</div>]
      <h1>{list}</h1> => 1
                         2
                         3
​
    1-5、{/* 1. 💥 函数本身也属于对象,函数本身不能放在插值表达式中 */}
         {/* 通常放函数的调用,函数的返回值也不能对象 */}
         {hello()}
​
    1-6、{/*  三元 */}
         <h2>{isShow ? 'show' : '不显示'}</h2>
​
    1-7、{/* 逻辑 */}
         <h3>{isShow && '显示'}</h3>
​
    1-8、{/* 💥 JSX本身也可以当做表达式放在插值符号中,往往配合其它的使用方式 */}
         <h4>{<span>123</span>}</h4>

2、条件、列表渲染

  • if else 语句

    function loadData() {
      if (isLoading) {
        return <h1>加载中...</h1>;
      } else {
        return <div>加载完毕</div>;
      }
    }
    ​
    {loadData()}
    
  • 三元表达式

    {isLoading ? <h1>加载中...</h1> : <h3>加载完毕</h3>}
    
  • 逻辑运算符

    {isLoading && <h1>加载中...</h1>}
    {!isLoading && <div>加载完毕</div>}
    
  • 列表渲染,类似v-for

    // 方法一
    const list = ['萝卜', '白菜', '橘子'];
    ​
    {list.map((item, index) => { // render 内部
          return (
            <h1
              // 💥key的作用:和v-forkey作用一模一样,都是提高更新效率
              // 🔔key的口诀:有idid ,没id用索引
              key={index}
            >
              {item}
            </h1>
          );
    })}
    ​
    // 方法二 
    const list = ['萝卜', '白菜', '橘子'];
    const listNode = list.map((item, index) => {
      return <h1 key={index}>{item}</h1>;
    });
    ​
    {listNode} // render 内部
    

3、行内样式

语法:style= {{css属性名:属性值}}
​
{/* 💥 React中不支持css属性连字符, 👍要求使用驼峰 */}
{/* 💥 px单位可以省略,值直接写数字 */}
<h1 style={{ color: 'red', fontSize: 12 }}>我想红</h1>

4、组件类型(函数式组件/类组件)

  • 函数式组件

    // 基本要求
    // 1.1 组件名称首字母必须大写
    // 1.2 函数必须返回一段JSX,如果不需要渲染任何内容,返回一个null// ✅ 组件
    function Hello() {
      return null;
    }
    ​
    {/* 使用上:💥React对大小写敏感 */}
    <Hello></Hello>
    
  • 类组件

    // 基本要求
    // 1. 组件名字必须大写字母开头
    // 2. render函数中必须返回一段JSX,如果不需要渲染任何内容,返回null
    // 3. 必须有render函数,否则报错,render函数类似函数式组件的语法// ✅ 组件
    class Hello extends React.Component {
      render() {
        return null;
      }
    }
    ​
    {/* 使用上:💥React对大小写敏感 */}
    <Hello></Hello>
    
    • extend继承
    // class的作用: 创建对象
    // 💥 属性 = 值class Programer {
      gender = '男';
    ​
      writeCode() {
        console.log('我代码写的贼6  ----->  ');
      }
    }
    ​
    // extends的作用:基础父类(爸爸)所有的属性和方法
    // 目的:复用相同的属性和方法,减少创建对象时消耗的内存
    class FrontEnd extends Programer {
      //  自动获得爸爸所有的属性和方法
      name = 'fe';
    }
    

5、状态 state

// 基本语法:state = {数据变量名: 值}, 可以声明多个数据
  state = {
    count: 100,
    msg: 'hello React',
  };
​
// 使用:获取state: 通过this.state.xxxx访问数据
// 💥 this 指向组件实例对象,与Vue类似
render(
   return (
      <h1>
        我是App - {this.state.count} - {this.state.msg}
      </h1>
   );
)

6、事件绑定

// 基础语法:on+大写驼峰事件名 = {函数名}
​
// 常见的两种写法:
// 👍1. 少量代码: 使用一个箭头函数
onClick={() => alert('123')}
​
onClick={this.handleClick}
​
// 注意:
// 1. 不能加():❌ onClick={this.handleClick()}
//  事件必须接受一个函数,而且要放在花括号中
// 2. 不能放在字符串中:❌onClick="alert('123')"

7、获取事件对象

// 获取事件对象 event
        <a
          // 💥 绑定事件的默认形参就是事件对象event, 类似Vue,
          // 1. onClick={(e) => e.preventDefault()}
          href="http://www.baidu.com"
          onClick={this.handleClick}
        >
          点我跳百度
        </a>
// 多行代码写法
  handleClick(e) {
    e.preventDefault();
    console.log('come here  ----->  ');
  }

8、事件传值

// 基础语法
onClick={(e) => this.handleClick(e, 2)}
​
// 自定义事件函数
  handleClick(e, num) {
    // 能够接收到外部传来的值
    console.log('num, e  ----->  ', num, e);
  }

9、修改状态 setState

// 基础语法:setState({更新的数据: 新的值})
this.setState({ msg: this.state.msg + '123', count: this.state.count + 100 });
​
// 💥注意:
// React中不可变数据-不允许直接修改state
this.state.count += 100;
​
this.setState({ count: (this.state.count += 100) });

10、不可变数据(即数组不可用方法修改state)

// 不可变数据: 不能使用会修改原数据的方法
// 如:push 、concat
// 如:this.state.count++// ✅setState 新值覆盖旧值
this.setState({ list: [...this.state.list, 5] });
​
// 注意:this.state.list.push(5);

11、受控组件

  • 受控组件 => 表单操做方式

      <input
              type="text"
              value={this.state.msg}
              // 💥React对表单的事件特殊处理,以后,所有表单的改变事件都用onChange
              onChange={(e) => {
                console.log('e.target.value  ----->  ', e.target.value);
                this.setState({ msg: e.target.value });
              }}
              // 💥 React中的失焦事件为onBlur
              onBlur={(e) => {
                console.log('e.target.value  ----->  ', e.target.value);
              }}
    />
    
  • 常见受控组件

    state = {
        name: 'zs',
        intro: '',
        city: '4',
        isSingle: true,
    };
    ​
    // input 
    <input type="text" value={name} onChange={this.handleChangeName} />
    ​
    // textarea 文本域
    <textarea value={intro} onChange={this.handleChangeIntro}></textarea>
    ​
    // select 下拉选择
    <select value={city} onChange={this.handleChangeCity}>
           <option value="1">北京</option>
           <option value="2">上海</option>
           <option value="3">广州</option>
           <option value="4">深圳</option>
    </select>
    ​
    // checkbox 单选框
    <input type="checkbox" checked={isSingle} onChange={this.handleChangeSingle} />
    

12、ref(获取dom元素、组件实例对象)

// 1. 创建 ref
  iptRef = React.createRef();
  childRef = React.createRef();
​
// 2. 绑定给dom元素或者组件
<input
  type="text"
  // 2. 绑定给dom元素或者组件
  ref={this.iptRef}
/>
<Child ref={this.childRef}></Child>
<button onClick={this.handleClick}>点我访问input元素,主动让input获得焦点</button>
​
// 3. 通过ref对象.current属性来访问
this.iptRef.current.focus();

13、props 组件通讯

  • 基本使用

    {/* 1. 传递数据: 给标签加上标签属性和值 */}
    <Child name="zs123" age={18123} gender={'xxxx'}></Child>
    ​
    // 函数式组件:通过形参接收props
    function Child({ name, age } = props) {
      return (
        <h1>
          我是子组件 - {name} -{age}
        </h1>
      );
    }
    ​
    // 类组件:通过this.props接收
    class Child2 extends React.Component {
      render() {
        const { name, age } = this.props;
        return (
          <div>
            <h1>{name}</h1>
            <h1>{age}</h1>
          </div>
        );
      }
    }
    
  • props的三个特点

    1. props不必先定义后使用
    2. props可以传递任意数据类型
         number string null undefined boolean object
         🔔 function  JSX
    3. props是只读的 - 类似vue中的单项数据流(修改需要调用父组件内的方法修改)
    
  • 父传子

    // 父组件
      state = {
        money: 1000,
      };
    ​
    // 父传子
    <Child money={this.state.money}></Child>
    ​
    // 子组件接收展示
    class Child extends React.Component {
      render() {
        const { money } = this.props;
        return (
          <div>
            <h1>爸爸给我钱了: {money} </h1>
          </div>
        );
      }
    }
    
  • 子传父

    // 父组件内部定义修改state的方法
    // 1. 父组件内定义一个函数
    handleCost = (num) => {
      console.log('num  ----->  ', num);
      this.setState({ money: this.state.money - num });
    };
    ​
    // 父传子
    <Child
      money={this.state.money}
      // 2. 通过props传函数给子组件
      handleCost={this.handleCost}
    ></Child>
    ​
    // 子组件:调用父组件传来的函数
    <button onClick={() => handleCost(8000)}>点我花爸爸的钱</button>
    
  • 兄弟通信

    // 整体思路
    兄弟组件通信,需要设计一个双方共同的父组件,然后数据及函数都由父组件集体管理,将父组件的state及fun传给兄弟组件,最终兄弟组件都是调用集体父组件的方法及state来达到通信的目的;
    
  • Context 跨组件通信

    // 1. React.createContext创建上下文, 解构出两个组件 Provider(提供者)Consumer(消费者)
    const { Provider, Consumer } = React.createContext();
    ​
    // 2. 使用Provider组件,传数据: 设置value属性传数据 -- 包住后代
    <Provider
      value="hello 我跨组件而来"
    >
      <Son></Son> // 子组件
    </Provider>
    ​
    // 3. 使用Consumer组件,接收数据
    {/* 💥 接受一个函数,返回需要返回一端JSX,形参就是Provider传来的数据 */}
    <Consumer> // 在孙组件之内
      {(data) => {
        return <h1>我是Consumer - {data}</h1>;
      }}
    </Consumer>
    ​
    // 优缺点:
      缺点:🔔增加组件的嵌套解构,难以阅读和理解
      优点:
        1. 🔔是React自带的跨组件通信方案,不需要额外下包
        2. 第三方的包喜欢用这个,减少体积
      场景🔔:
        1. 多语言切换
        2. 一键换色
    

14、props深入使用(插槽、校验)

  • childre(类似vue的插槽)

  • 插槽的定义:让父组件可以向子组件指定位置插入html结构,也是一种组件间通信的方式

    // 第一步设置插槽组件
    function Child({ children }: any) {
      return <h1>{children}</h1>
    }
    ​
    // 第二步
    {/* 数组、原始数据类型  */}
    <Child>{[1, 2, 3, 4]}</Child>
    {/* 💥函数  */}
    <Child2>
      {() => {
        return <i>我是children传来的函数</i>
      }}
    </Child2>
    {/* 💥JSX */}
    <Child>
      <ul>
        <li>123</li>
        <li>456</li>
      </ul>
    </Child>
    
  • childre模拟 匿名插槽&具名插槽&作用域插槽

    // 匿名插槽
    // 父组件内:
    <Child>{this.state.msg}</Child>
    ​
    // 子租件:
    class Child extends Component {
      render() {
        console.log('this  ----->  ', this);
        return <h1>Child子组件 - {this.props.children}</h1>;
      }
    }
    ​
    // 具名插槽
    // 父组件内:
    <ChildHasName>
      {{
        header: '蜀道难',
        footer: '难于上青天',
      }}
    </ChildHasName>
    ​
    // 子组件内:
    class ChildHasName extends Component {
      render() {
        return (
          <div>
            <header>显示header: {this.props.children.header}</header>
            <footer>显示footer: {this.props.children.footer}</footer>
          </div>
        );
      }
    }
    ​
    // 作用域插槽
    // 父组件内:
    <ChildScope>
      {(data) => {
        return (
          <ul>
            {data.map((item) => {
              return <li key={item}>{item}</li>;
            })}
          </ul>
        );
      }}
    </ChildScope>
    ​
    // 子组件:
    class ChildScope extends Component {
      state = {
        list: ['苹果', '橘子', '香蕉'],
      };
    ​
      render() {
        return <h1>Child子组件 - {this.props.children(this.state.list)}</h1>;
      }
    }
    
  • 利用props校验

    // 1. 导入包props-type,提供类型 安装:npm install prop-types --save
    import PropTypes from 'prop-types';
    ​
    // 2. 给组件设置规则对象
    // 💥 组件名.propTypes = { 规则}
    Child.propTypes = {
      msg: PropTypes.string.isRequired,
      list: PropTypes.array,
      person: PropTypes.object,
      isShow: PropTypes.bool,
      handleClick: PropTypes.func,
      // 💥 JSX
      title: PropTypes.element,
      zs: PropTypes.shape({
        name: PropTypes.string,
        age: PropTypes.number,
      }),
    };
    ​
    // 3. 组件无需额外操作,父组件传值时需要按照规则传值
    class Child extends React.Component {
      render() {
        return <div></div>;
      }
    }
    
  • defaultProps 默认值

    // 第一步 设置defaultProps 默认值
    ChildSon).defaultProps = {
      msg: 'hello Child',
    };
    ​
    // 第二步 组件内用 props 获取
    class Child extends React.Component {
      render() {
        return <div>{this.props.msg}</div>;
      }
    } // 类组件
    ​
    function Son({ msg = 'hello Son' }) {
      return <h1>{msg}</h1>;
    } // 函数组件
    
  • static关键字

    // 类内部使用 static 关键字之后不会被子类继承,也不会初始化到实例对象中// 使用
    class Child extends React.Component {
      static propTypes = {
        msg: PropTypes.string.isRequired,
        list: PropTypes.array,
        person: PropTypes.object,
        isShow: PropTypes.bool,
        handleClick: PropTypes.func,
        // 💥 JSX
        title: PropTypes.element,
        zs: PropTypes.shape({
          name: PropTypes.string,
          age: PropTypes.number,
        }),
      };
    ​
      static defaultProps = {
        hello: '123',
      };
    ​
      render() {
        return <div>{this.props.hello}</div>;
      }
    }
    

15、React 的生命周期

一文让你快速上手React18,最直白知识点总结

详细讲述React生命周期文章地址:juejin.cn/post/696618…

1. 挂载阶段 - 
  1.1 constructor: 创建时,对标vue中的 created
      💥 早期声明state和ref,现在都简写
  1.2 render:渲染dom
  1.3 componentDidMount: 挂载后,对标vue中的mounted
      💥 发请求、最先能够获取DOM、定时器、绑定事件
      
2. 更新阶段
  2.1 render: 重新渲染dom
  2.2 compoentDidUpdate:更新后,对标vue中的updated
     💥 可以获取到更新后的值,常用来做缓存
     
3. 卸载阶段 -
  3.1 componentWillUnMount
    💥 做清除的动作,如:清除定时器、取消监听的事件
执行次数:
  constructor和componentDidMount只会执行一次
  componentWillUnMount只会执行一次

16、Hooks (重要)

16-1、useState基本使用

// 1. 导入 useState钩子函数: 声明状态
import React, { useState } from 'react';
​
// 2. 调用钩子函数
const [count, setCount] = useState(100);
// count 等价于 this.state.couont
// setCount等价于this.setState({couont: xxx})// 3、使用
return (
  <div>
    <h1>{count}</h1>
    <button
      onClick={() => {
        setCount(count + 1);
      }}
    >
      点我加+1
    </button>
  </div>
);

16-2、useState注意事项

// 1. useState可以多次调用,声明多个状态
  const [count, setCount] = useState(100);
  const [msg, setMsg] = useState('hello React');
// 2. useState可声明任意数据类型
  const [list, setList] = useState([1, 2, 3]);
  const [person, setPerson] = useState({ name: 'zs', age: 18 });
// 3. 更新状态,还是新值覆盖旧值
  setPerson({ ...person, name: 'ww' });
  // ❌person.name = '王五';

16-3、Hooks的三个使用限制

// 1. hooks不能放在if语句中
  if (false) {
    // 💥React 是根据useState调用顺序,保证每个状态的值分配。React要求调用顺序永远不变
    const [hello, setHello] = useState('123');
  }
​
// 2. 不能放在for语句中
  for (let index = 0; index < list.length; index++) {
    const [hello, setHello] = useState('123');
  }
​
// 3. 普通函数中不能使用hooks
function hello(params) {
  const [state, setState] = useState('xxxx');
  return null;
}

16-4、受控组件

// 1. 声明state
const [value, setValue] = useState('');
​
// 2. 控制value
<input
  type="text"
  value={value}
  // 💥 绑定方法没有this了
  onChange={handleChange}
/>
​
// 3. 声明方法用const
const handleChange = (e) => {
    setValue(e.target.value);
};

16-5、useEffect基本使用

// 第一步 引入 useEffect
import React, { useState, useEffect } from 'react';
​
// 第二步 导入useEffect钩子函数: 会自动执行
// 语法: useEffect(回调函数);  回调函数中可以获取到更新后的值
useEffect(() => {
   document.title = count;
});

16-6、useEffect模拟生命周期

// 1. 第二个参数为数组:空数组时代表的是挂载时。等价于componentDidMount
useEffect(() => {
  console.log('我是挂载时的钩子函数,只会执行一次  ----->  ');
  document.title = count;
}, []);
​
// 2. 第二个参数为数组:数组内有依赖项(状态)时,
// 💥代表的是挂载时和更新时二合一, 类似Vue中的watch属性,开启了immediately
// 💥等价于componentDidMount 和 componentDidUpdate 二合一
useEffect(() => {
   console.log('我是更新时的钩子函数  ----->  ');
   document.title = count;
}, [count]);
​
// 3、通过返回一个函数,模拟卸载时的生命周期
// 语法: useEffect回调函数内,返回一个函数,这个函数就是卸载时会自动执行的钩子函数
useEffect(() => {
  return () => {
    console.log('我要被卸载掉了  ----->  ');
  };
}, []);

16-7、useEffect监听多个状态

// 第一种 合并写法,需要根据多个状态的值,进行计算
useEffect(() => {
  document.title = count + msg;
}, [count, msg]);
​
// 第二种 分开监听,不需要使用多个状态计算
useEffect(() => {
  console.log('count  ----->  ', count);
}, [count]);
​
useEffect(() => {
  console.log('msg  ----->  ', msg);
}, [msg]);

16-8、useEffect挂载与卸载综合写法

useEffect(() => {
  const fn = () => {
    console.log('浏览器窗口被调整了');
  };
  // 2. 同一个逻辑的挂载和卸载,合在一个useEffect里起来写
  window.addEventListener('resize', fn);
  return () => {
    window.removeEventListener('resize', fn);
  };
}, []);

16-9、useEffect 与 axios 请求

// 第一步 封装一个异步函数
useEffect(() => {
  loadData();
}, []);
​
// 第二步 声明函数,请求回值并赋值
const loadData = async () => {
  const res = await getChannelsAPI();
  setList(res.data.channels);
};

16-10 父组件调用子组件身上的方法

子组件

// 第一步 引入 forwardRef, useImperativeHandle (关键)
import React, { useState, useEffect, forwardRef, useImperativeHandle } from 'react'

// 第二步 使用 useImperativeHandle 创建方法暴露给父组件
const DelTabs  = (props: any, ref: any) => { 
  const [test, setTest] = useState(11)
  useImperativeHandle(ref, () => ({
    changeTest: (newVal: any) => {
      setTest(newVal)
    },
  }))

  useEffect(() => {
    console.log(26, test)
  }, [test]) // 监听父组件用子组件的方法更改子组件身上的数据状态
}

// 第三步 使用 forwardRef 包裹子组件并暴露
export default forwardRef(DelTabs)

父组件

// 第一步 引入 useRef 
import React, { useRef } from 'react'

// 第二步 useRef()
  const childRef = useRef() as any
  const btnTestClick = () => {
    childRef.current.changeTest('father')
  }

// 第三步 子组件身上绑定 ref
<DelTabs ref={childRef} />

16-11 子组件调用父组件身上的方法

父组件

// 将父组件身上的方法传递给子组件
<DelTabs delTabFn={delTabFn} />

子组件

// 子组件用props接收父组件传递的方法
const DelTabs = (props: any) => {

// 使用父组件的方法
<div onClick={(e) => props.delTabFn(e, pathname)}>
  <CloseOutlined style={{ marginRight: 10 }} />
  关闭当前
</div>
转载自:https://juejin.cn/post/7124478266819215368
评论
请登录