React V7.0.2学习笔记

react 的特点

  • 声明式: 声明式编写 UI,易于维护
  • 组件化: 组件逻辑使用js而非模版,轻松传递数据,保持状态和DOM分离
  • 跨平台:可以在其他语言基础上引入react,使用node服务渲染,React Native 开发原生移动应用

render

功能

  • 接受输入的数据,并返回要展示的内容;
  • 当状态数据改变时,组件会调用,并重新渲染对应的标记

dangerouslySetInnerHTML

功能

  • React 为浏览器 DOM 提供 innerHTML 的替换方案,存在安全隐患

Cross-Site Scripting(跨站脚本攻击)简称 XSS,是一种代码注入攻击,注入XSS 代码到网页,窃取用户数据并发送到攻击者的网站。
React 如何防止 XSS 攻击: 引申
- 自动转义,React DOM 在渲染所有输入内容之前,默认会进行转义;
- 改造React.createElement()的Children,防止存储型的 XSS 攻击。

React简单引入

<!-- 加载 React-->
  <script src="https://unpkg.com/react@17/umd/react.development.js" crossorigin></script>
  <script src="https://unpkg.com/react-dom@17/umd/react-dom.development.js" crossorigin></script>
 
<!-- script标签内使用 JSX,type="text/babel" -->
<script src="https://unpkg.com/babel-standalone@6/babel.min.js"></script>
<script type="text/babel">
      ReactDOM.render(
        <h1>Hello, world!</h1>,
        document.getElementById('root')
      );
</script>

React命令安装

npx create-react-app demo
生成压缩文件

  1. 安装node
  2. 运行 npm init -y
  3. 运行 npm install terser
  4. 运行 npx terser -c -m -o xxx.min.js – xxx.js

将 JSX 添加到项目(安装 JSX 预处理器)

  1. npm install babel-cli@6 babel-preset-react-app@3

更新react项目的默认端口

.env文件
  • 项目根目录下新建.env文件
  • 写入PORT=3005,并重启(注意加入忽略文件)
安装cross-env
  • 安装cross-env,注意加 -D npm i -D cross-env
  • 更新启动命令"start": "cross-env PORT=3006 react-scripts start",并重启
更新start.js
  • 路径: /node_modules/react-scripts/scripts/start.js
  • 更新变量DEFAULT_PORT的默认配置
    const DEFAULT_PORT = parseInt(process.env.PORT, 10) || 3006;

ref的使用

  • class 组件: 使用 ref 属性
  • 函数组件:使用 ref属性,子组件接收ref,使用React.forwardRef((props, ref) => {})
    • useImperativeHandle 自定暴露给父组件
  • 回调 Refs,将组件实例或 HTML DOM 元素存在state中

何时使用 Refs
管理焦点,文本选择或媒体播放
触发强制动画
集成第三方 DOM 库

一般情况:父子之间使用

父组件 createRef.current 将指向子组件<input/>

const A = () => {
  const ref1 = React.createRef();
  const ref2 = React.createRef();
  const ref3 = React.createRef();

  return (
    <>
      <div
        onClick={() => {
          console.log(ref1.current);
          console.log(ref2.current);
          console.log(ref3.current);
        }}
      >
        --
      </div>
      <B ref={ref1} />
      <C ref={ref2} />
      <D ref={ref3} />
    </>
  );
};
// 函数子组件
const B = React.forwardRef((props, ref) => {
  return <input ref={ref} />;
});
// class子组件
class C extends React.Component {
  constructor(props) {
    super(props);
    this.state = {
      a: 1,
    };
  }
  render() {
    return <p>123424</p>;
  }
}
// 函数子组件,控制暴露范围
const D = React.forwardRef((props, ref) => {
  const [val, setVal] = useState("");
  useImperativeHandle(ref, () => ({
    val,
  }));
  return (
    <input value={val} ref={ref} onChange={(e) => setVal(e?.target?.value)} />
  );
});

在这里插入图片描述

特殊情况:爷孙之间使用

父组件 createRef.current 将指向孙子组件<input/>

// 父组件
const A = () => {
    const ref2 = React.createRef();

    return <>
        <div onClick={() =>{console.log(ref2.current.value)}} >--</div>
        <C ref={ref2} />
    </>
}
const D = (props) => {
    return <input ref={props.myRef}/>
}
const C = React.forwardRef((props, ref) => {
    return <D myRef={ref}/>
})

回调 Refs

element存入state

const A = () => {
  const [ref, setref] = useState(null);

  return (
    <>
      <div
        onClick={() => {
          ref.value = "23";
          ref.focus();
        }}
      >
        input内容更新,并获取焦点
      </div>
      <input placeholder="请输入" ref={(el) => setref(el)} />
    </>
  );
};

高阶组件

HOC(高阶组件) 是纯函数,没有副作用。命名withX
HOC是参数为组件,返回值为新组件的函数。 通过将组件包装在容器组件中来组成新组件
HOC 不需要关心数据的使用方式或原因,而被包装组件也不需要关心数据是怎么来的
静态方法,务必手动复制
ref需要通过React.forwardRef加入到props中
样例实现了定时刷新数据的功能

import * as React from "react";
import { useState, useEffect } from "react";

const Test01: React.FC = (props) => {
  return (
    <ul style={{ background: "red", color: "#fff" }}>
      {props?.data?.map((item: string) => (
        <li key={item}> {item}</li>
      ))}
    </ul>
  );
};
const Test02: React.FC = (props) => {
  return <div style={{ background: "aqua" }}>{props.data}</div>;
};

function withGetData(WrapperComponent: React.FC | React.Component, fun: Function) {
  return class extends React.Component<any, any> {
    constructor(props: any) {
      super(props);
      this.state = {
        data: null,
      };
    }

    componentDidMount(): void {
      setInterval(() => {
        let data = fun();
        this.setState({
          data,
        });
      }, 1000);
    }
    render() {
      return (
        <WrapperComponent
          {...this.props}
          {...this.state}
        />
      );
    }
  };
}

const Test = () => {
  const getData1 = () => {
    var result = [];
    for (var i = Math.ceil(Math.random() * 10); i > 0; --i)
      result.push(getData2());
    return result;
  };
  const getData2 = () => {
    const str =
      "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";
    var result = "";
    for (var i = Math.ceil(Math.random() * 100); i > 0; --i)
      result += str[Math.floor(Math.random() * str.length)];
    return result;
  };

  const Test1 = withGetData(Test01, getData1);
  const Test2 = withGetData(Test02, getData2);
  return (
    <>
      <Test1 />
      <Test2 />
    </>
  );
};
export default Test;

Portals

  • 可以实现将子节点渲染到非父组件节点
  • 父组件可以接受到事件冒泡
import { createPortal } from "react-dom";
// 组件
const PortalTest = () => {
  let warppers = document.getElementsByClassName("protal-test");
  let el = null;
  if (warppers.length > 0) {
    el = warppers[0];
  } else {
    el = document.createElement("div");
    el.className = "protal-test";
    document.getElementsByClassName("app-root")[0].appendChild(el);
  }

  return createPortal(
    <div
      style={{
        position: "fixed",
        width: 100,
        height: "100px",
        background: "red",
        top: 0,
      }}
    ></div>,
    el
  );
};
// 使用
<div>
    <Test />
</div>

不使用 JSX 的 React 写法

  • ·React.createElement(‘div’, null, `Hello ${this.props.toWhat}`);
  • react-hyperscript
  • hyperscript-helpers

diff算法

  • React 引入了 key 属性,React 使用 key 来匹配原有树上的子元素以及最新树上的子元素
  • 不推荐数组中的下标作为 key

Render Props

  • 将函数组件放入props中
const A = () => {
  return (
    <>
      <B
        child={([x, y]) => (
          <img
            src="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9Ii0xMS41IC0xMC4yMzE3NCAyMyAyMC40NjM0OCI+CiAgPHRpdGxlPlJlYWN0IExvZ288L3RpdGxlPgogIDxjaXJjbGUgY3g9IjAiIGN5PSIwIiByPSIyLjA1IiBmaWxsPSIjNjFkYWZiIi8+CiAgPGcgc3Ryb2tlPSIjNjFkYWZiIiBzdHJva2Utd2lkdGg9IjEiIGZpbGw9Im5vbmUiPgogICAgPGVsbGlwc2Ugcng9IjExIiByeT0iNC4yIi8+CiAgICA8ZWxsaXBzZSByeD0iMTEiIHJ5PSI0LjIiIHRyYW5zZm9ybT0icm90YXRlKDYwKSIvPgogICAgPGVsbGlwc2Ugcng9IjExIiByeT0iNC4yIiB0cmFuc2Zvcm09InJvdGF0ZSgxMjApIi8+CiAgPC9nPgo8L3N2Zz4K"
            style={{ width: "100px", position: "absolute", left: x, top: y }}
          />
        )}
      />
    </>
  );
};
const B = (props) => {
  const [position, setposition] = useState([0, 0]);
  const handleMove = ({ clientX, clientY }) => {
    setposition([clientX, clientY]);
  };
  return (
    <>
      <div style={{ height: "100vh" }} onMouseMove={handleMove}>
        移动鼠标
      </div>
      {props.child(position)}
    </>
  );
};

静态类型检查

  • Flow
  • TypeScript

引入方式

  • ES6 与 npm 时,import React from 'react'
  • ES5 与 npm 时,var React = require('react')

高阶组件/API

详见:React 顶层 API
React.memo: 使用场景相同 props 的情况下渲染相同的结果

  • 仅检查 props 变更,重新渲染
  • 当 state 或 context 发生变化时,重新渲染
  • 默认只对对象浅层对比,通过第二个参数(props 相等,返回 true;props 不等,返回 false)传入来实现对比过程控制
//areEqual返回boolean值,props 相等返回 true
export default React.memo(MyComponent, areEqual);

React.Component:并未实现 shouldComponentUpdate;深对比props和state
React.PureComponent浅对比 prop 和 state;实现的shouldComponentUpdate,将跳过所有子组件树的 prop 更新,需要确保所有子组件也都是“纯”的组件
React.Children

  • map:this.props.children.map 可能会报错,但React.Children.map不会
  • forEach: 类似map ,但没有返回
  • count:一个组件有多少个children
  • toArray:为每个子节点分配一个 key
  • only:只有一个child
const A = (props) => {
  console.log(props.children);
  console.log(React.Children.count(props.children), 1);
  console.log(React.Children.toArray(props.children), 2);
  return (
      <div>
      ---
      {React.Children.map(props.children, (child) => (child)
              
      )}
      </div>
  );
};
<A>
  <p>qsdwq</p>
  <div>wcfevg1</div>
  <div>wcfevg2</div>
  <div>wcfevg3</div>
  {() => <h1>First</h1>}

</A>

在这里插入图片描述
在这里插入图片描述

生命周期执行顺序

挂载
constructor:在 React 组件挂载之前
getDerivedStateFromProps
render
componentDidMount:在组件挂载后(插入 DOM 树中)立即调用
更新
static getDerivedStateFromProps:
shouldComponentUpdate:更新后会被立即调用,首次渲染不会执行此方法,跳过所有子组件树的 prop 更新
render
static getSnapshotBeforeUpdate:(不常用),则它的返回值将作为 componentDidUpdate的第三个参数 “snapshot
componentDidUpdate
卸载
componentWillUnmount:组件卸载及销毁之前直接调用
错误处理
static getDerivedStateFromError
componentDidCatch

render()

  • 纯函数,不会直接与浏览器交互
  • 检查 this.props 和 this.state 的变化并返回元素
  • 如果 shouldComponentUpdate() 返回 false,则不会调用 render()

constructor()

  • 如果不初始化 state 或不进行方法绑定,则不需要为 React 组件实现构造函数。
  • constructor中不要调用 setState() 方法
  • 避免将 props 的值复制给 state!

React元素类型

React 元素
数组或 fragments
Portals
字符串或数值类型
布尔类型或 null(test && 的模式,其中 test 为布尔类型)

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值