IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> JavaScript知识库 -> React快速暴力入门(四) -> 正文阅读

[JavaScript知识库]React快速暴力入门(四)

React快速暴力入门(四)

一.了解与使用 react-redux

前面也说了,react-redux 其实就是 redux的升级版,对许多地方进行了优化,但在学习他之前,需要我们进行一些对 redux的优化知识。
在这里插入图片描述
1. 使用容器组件和UI组件
其目的就是为了把组件身上太多的活进行拆分,分为UI组件(内组件)和容器组件(外组件),两个组件之间使用 props进行通信,对 store那边的请求状态,更改状态的活交给容器组件来干,而通过状态来编写页面,更新渲染等活,就交给 UI组件来干。
了解了这个后,就可以开始使用 react-redux了
2. 安装 react-redux

npm i react-redux -S

这个就不多说了
3. 创建文件夹
对于容器组件,我们都是使用 containers文件夹进行存储。

  • containers ------ 用于存储容器组件的文件夹
  • redux ------ 用于存储 react-redux相关的文件夹

4. 创建容器组件
容器组件通过 react-redux的 connect() 方法进行创建。

// 引入 Count的 UI组件
import Count from "../../components/Count";
// 引入 connect用于连接 UI组件与 redux
import { connect } from 'react-redux'

// 该函数的返回值作为状态传递给 UI组件
function mapStateToProps(state){
    return {
        count: state,
    }
}
// 该函数的返回值作为操作状态的方法传递给 UI组件
function mapDispatchToProps(dispatch){
    return {
        add: data=>{
            console.log(1234);
        }
    }
}

export default connect(mapStateToProps, mapDispatchToProps)(Count);

使用 connect( )( )创建并暴露一个 Count组件的容器组件。
调用 connect时有两个参数,且必须时函数。

  • mapStateToProps( state ): 该函数的返回值会作为 状态 传递给 UI组件
    state: 参数 state 为 react-redux 默认操作好的 store.getState()
  • mapDispatchToProps( dispatch ): 该函数的返回值会作为 操作状态的方法 传递给 UI组件,有语法糖写法,传入个对象即可(看下面代码)。
    dispatch: 参数 dispatch 为 react-redux 默认给的 store.dispatch 方法

这里只是为了讲解方便,使用时可以使用语法糖的。
注意的几个地方:

  1. 一般都是把 UI组件 与 容器组件 写在一个文件中,至于存在哪个文件夹中看公司需求(一般都是 containers)
  2. 容器组件里的 store不是通过引入使用,而是作为 props传递给容器组件的标签的。
  3. 语法糖写法:
// 引入 Count的 UI组件
import Count from "../../components/Count";
// 引入 connect用于连接 UI组件与 redux
import { connect } from 'react-redux'

// 精简写法
export default connect(
    state => ({count: state}),
    {
        add: data=> console.log(1234, data)
    }
)(Count);

5. Provider 组件
如果你有许多个容器组件,那么每个容器组件都要传入 store,那么是不是觉得太繁琐了呢?所以 react-redux 提供了 Provider组件用于处理这个问题,只需要在根标签处使用并把 store传递进去,他就可以自动判断哪些组件需要使用 store并自动传递给它。

import React from 'react';
import ReactDOM from 'react-dom';
import './index.css';
import App from './App';
import store from "./redux/store"
// 优化3: 使用自带的 Provider自动判断哪些组件需要使用 store,从而自动导入
import { Provider } from "react-redux";
ReactDOM.render(
  <Provider store={store}>
    <App />
  </Provider>,
  document.getElementById('root')
);

6. 监听状态变化
有没有发现,我上面的代码已经没有再写 store.subscribe() 监听状态变化了,是因为我们创建容器组件的 connect已经帮我们进行监听了。
7. 容器组件之间通信
其实这个很简单,还记的 connect的第一个函数的第一个参数吗?传递给 UI组件的是 props,他的参数是 state,这个 state是 store.getState()。
可是此时你的 store.getState() 不再是一个单纯的值,而是所有 reducer的对象,所以我们可以在里面获取到其他容器组件的值

export default connect(
    state => ({count: state.count, personLength: state.persons.length}),
    {
        increment: createIncrementAction,
        decrement: createDecrementAction,
        asyncIncrement: createIncrementAsyncAction,
    }
)(Count);

8. 文件规范
在这里插入图片描述
9. 再说一遍,能不用就别用这东西!!

二.React16.8 与一些扩展

1. lazy() 与 Suspense
之前的组件,都是一并加载的,这样会给服务器带来较大的负担,所以 react推出了 lazy()懒加载,进行组件的按需加载。
与之一起出来的是 Suspense,他解决的是组件在加载过程中还没加载出来时的白屏,用于展示其他的内容。

import React, { Component, lazy, Suspense } from 'react'
import {Route, Link} from "react-router-dom"

// import Home from "./Home";
// import About from "./About";
import Loading from "./Loading";
// lazy() 方法的参数是一个函数,返回需要加载的组件
const Home = lazy(()=>import("./Home"))
const About = lazy(()=>import("./About"))
export default class index extends Component {
    render() {
        return (
            <div>
                <Link to="/home">Home</Link>
                <Link to="/about">About</Link>
                <hr/>
                {/* Suspense 用于解决加载组件时的白屏,可以显示其他的内容,而其他内容不允许使用 lazy加载 */}
                <Suspense fallback={<Loading/>}>
                    <Route path="/home" component={Home}></Route>
                    <Route path="/about" component={About}></Route>
                </Suspense>
            </div>
        )
    }
}

  • lazy 的参数是一个函数,使用 import 导入一个组件并返回
  • Suspense 的属性 fallback属性的属性值是 组件标签 而不是组件
  • Suspense所使用的组件不能使用 lazy进行懒加载。

2. Hook
React16.8可以说是给函数式组件一次春天,因为他有了Hook,可以实现一些 state、生命周期函数,refs等特性。
让我们一个个来看:
(1) stateHook
可以让函数式组件实现使用 state的特性:


export default function Demo() {
    // useState返回一个数组,只有两个元素(只有两个元素)
    // 元素1 为状态,元素2 为更新状态的方法
    // 第一次调用时以及将count进行底层存储,所以 Demo重复渲染不会重置count数据
    const [count, setCount] = React.useState(0); // 初始值赋为 0
    const [name, setName] = React.useState("Tom");
    function add(){
        // 进行状态赋值
        // setCount(count + 1); // 写法1,直接将原来的状态值覆盖
        setCount(count=> count+1); // 写法2,参数为函数,接受原本的状态值,返回新的状态值,覆盖原来的状态
    }
    return (
        <div>
            <h3>名字:{name}</h3>
            <h2>当前求和:{count}</h2>
            <button onClick={add}>点击加一</button>    
        </div>
    )
}

(2) EffectHook
可以让函数式组件实现类似生命周期钩子的特性:

export default function Demo() {
    const [count, setCount] = React.useState(0);
    const [name, setName] = React.useState("Tom");
    function add(){
        setCount(count=> count+1);
    }
    function updName(){
        setName(name=>"Jerry");
    }
    function unmount(){
        ReactDOM.unmountComponentAtNode(document.querySelector("#root"));
    }
    // useEffect接收两个参数,第一个为函数体,第二个为检测的对象(数组),当检测的对象状态发生改变,就会触发函数
    // 不填写第二参数时,检测所有元素,相当于 componentDidUpdate生命周期函数
    React.useEffect(()=>{
        // console.log("asdf")
        let timer = setInterval(()=>{
            setCount(count=>count+1);
        },1000);
        return ()=>{        // 在 useEffect中的函数体里返回的函数,相当于 componentWillUnmount生命周期函数
            console.log("unmount")
            clearInterval(timer);
;        }
    },[]) // 数组为空是谁也不检测,只执行一次函数,相当于生命周期函数的 componentDidMount
    return (
        <div>
            <h2>当前求和:{count}</h2>
            <button onClick={add}>点击加一</button>
            <h2>当前名字:{name}</h2>
            <button onClick={updName}>修改名字为Jerry</button>
            <button onClick={unmount}>卸载组件</button>
        </div>
    )
}

可以把 useEffect Hook 看作三个函数的结合:

  • componentDidMount
  • componentDidUpdate
  • componentWillUnmount

(3) refHook
refHook 可以让函数式组件实现类似 ref的特性

export default function Demo() {
    const [count, setCount] = React.useState(0);
    function add(){
        setCount(count=> count+1);
    }
    function show(){
        // 获取文本框内容
        alert(myInput.current.value);
    }
    // 生成一个容器
    const myInput = React.useRef();
    return (
        <div>
            <h2>当前求和:{count}</h2>
            {/* 绑定容器 */}
            <input type="text" ref={myInput}/>
            <button onClick={add}>点击加一</button>
            <button onClick={show}>点击展示数据</button>
        </div>
    )
}

这个就没啥难度。
3. Fragment
在 react渲染组件的时候,当你的组件越来越多时,你有没有发现你的 DOM层级越来越多,有些不美观,所以就出现了 Fragment。
可以使用 Fragment标签代替组件的根标签,在 React解析的时候会被处理掉,
从而让生成出来的代码的层级更加简洁。

export default class Demo extends Component {
    render() {
        return (
            // 使用空标签可以达到一样的效果,但是空标签不允许包含任何的属性
            <Fragment key={1}>
                <input type="text"/>
                <input type="text"/>
            </Fragment>
        )
    }
}

4. Context
Context是一种新的组件通信方式,常用于【祖组件】和【后代组件】之间通信。

// 1. 创建一个 Context容器对象
const UserNameContext = React.createContext();
// 1.1 拿到 Provider与 Consumer属性
const {Provider, Consumer} = UserNameContext;
export default class A extends Component {
    state={username: "Tom"}
    render() {
        return (
            <div className="a">
                <h1>我是A组件</h1>
                <p>我的用户名是:{this.state.username}</p>
                {/* 2 使用组件,后代组件都能收到来自 value的值,就在 this上的 context属性上(需提前声明)  */}
                <Provider value={this.state.username}>
                    <B/>
                </Provider>
            </div>
        )
    }
}
class B extends Component {
    // 3. 声明接受 Context
    static contextType = UserNameContext;   // 此方法只适用于 类组件
    render() {
        console.log(this.context);  // Tom
        return (
            <div className="b">
                <h2>我是B组件</h2>
                <p>A的用户名是:{this.context}</p>
                <C/>
            </div>
        )
    }
}
function C() {
    return (
        <div>
             <div className="c">
                <h3>我是C组件</h3>         
                {/* 3.2 使用 Consumer组件进行声明接受(类组件和函数式组件都可以)  */}
                <Consumer>
                    {value=> ("A的用户名是:" + value)}
                </Consumer>
             </div>
        </div>
    )
}

5. PureComponent
在组件中,只要执行了 setState(),即使没更新状态数据,组件也会重新 render()。
只要组件重新 render(),就会自动重新 render()子组件,纵使子组件没有使用到父组件任何数据。
这两种情况都会导致重复渲染,使得效率低下。
效率高的做法:只有组件的state或props发生变化时菜重新render()。

  • 解决方法1:通过生命周期函数 shouldComponentUpdate() 进行数据判断在进行重新渲染
  • 解决方法2:类组件通过继承 PureComponent组件,自动进行数据判断(浅对比—判断地址值)(常用)
import React, { Component, PureComponent } from 'react'
export default class Parent extends PureComponent {
    state = {carName: "奔驰"}
    changeCar = ()=>{
        this.setState(state=>({carName:"迈巴赫"}));
    }
    // shouldComponentUpdate有两个参数,分别是准备修改的 props和 state
    // shouldComponentUp date(nextProps, nextState){
    //     console.log(nextProps, nextState);  // 目标要修改的props和state
    //     console.log(this.props, this.state);   // 还未修改原本的props和state
    //     return !this.state.carName === nextState.carName;
    // }
    render() {
        console.log("parent render");
        return (
            <div className="parent">
                <h1>Parent</h1>
                <p>我的车是:{this.state.carName}</p>
                <button onClick={this.changeCar}>点击换车</button>
                <Child/>
            </div>
        )
    }
}
class Child extends PureComponent {
    
    // shouldComponentUpdate(nextProps){
    //     return !this.props.carName === nextProps.carName
    // }
    render() {
        console.log("child render");
        return (
            <div className="child">
                <h2>Child</h2>
                {/* <p>父亲的车是:{this.props.carName}</p> */}
            </div>
        )
    }
}

6. 父子组件
不多说,直接上代码:

import React, { Component, PureComponent } from 'react'
import "./index.css"

export default class Parent extends PureComponent {
    state = {carName: "奔驰"}
    changeCar = ()=>{
        this.setState(state=>({carName:"迈巴赫"}));
    }
    render() {
        console.log("parent render");
        return (
            <div className="parent">
                <h1>Parent</h1>
                <p>我的车是:{this.state.carName}</p>
                {/* A组件 与 B组件 形成父子组件的第二种方法 */}
                {/* <A>
                    <B/>
                </A> */}
                {/* 类似于 Vue的插槽 */}
                <A render={(name)=><B name={name}/>}/>  
            </div>
        )
    }
}

class A extends PureComponent {
    render() {
        console.log("A render");
        return (
            <div className="a">
                <h2>A</h2>
                {/* A组件 与 B组件 形成父子组件的第一种方式 */}
                {/* <B/> */}
                {/* {this.props.children} */}
                {this.props.render("Tom")}
            </div>
        )
    }
}
class B extends PureComponent {
    render() {
        console.log("B render");
        return (
            <div className="b">
                <h2>B</h2>
                <p>{this.props.name}</p>
            </div>
        )
    }
}

7. ErrorBoundary
当你的组件存在父子组件关系时,如果说你的子组件出现了错误,那么会导致父组件一并崩掉,那有没有什么办法,可以把错误控制在一个组件里,不让他扩散呢?
答案是有的,有两个函数:

  • getDerivedStateFromError(error)
  • componentDidCatch(error, info)

上代码:

import React, { Component, Fragment } from 'react'
import Child from "./Child";
// 错误边界即把组件的错误信息控制在一个组件中,不使他扩散而导致程序崩溃
export default class Person extends Component {
    state = {
        hasError: "",   // 用于标识子组件是否产生错误
    }
    // 当子组件发生错误时会触发该生命周期函数,且参数为错误信息
    // 只适用于生产环境,只能捕获后代组件生命周期产生的错误
    static getDerivedStateFromError(error){
        // 一般用于处理错误出现时返回给用户展示的东西
        console.log("出错了");
        console.log(error);
    }
    // 组件渲染过程中出错就会触发该生命周期函数
    componentDidCatch(error, info){
        // 一般用于统计错误,反馈给雾浮起,用于通知程序员进行bug修改
        console.log("渲染组件出错");
        console.log(error, info)
    }
    render() {
        return (
            <Fragment>
                <h2>我是Parent组件</h2>
                {
                    this.state.hasError ? 
                        <h2>当前网络不大行,建议买高级网络套餐好吧</h2> :
                        <Child/>                
                }
            </Fragment>
        )
    }
}

8. 组件通信方式总结

  1. 组件之间的关系:
  • 父子组件
  • 兄弟组件
  • 祖孙组件(跨级组件)
  1. 几种通信方式:
  • props
  • children props
  • render props
  1. 消息订阅
  • pubsub, event
  • 集中式管理
  • redux, dva, react-redux
  • conText生产者, 消费者模式
  1. 较好的搭配方式:
  • 父子组件:props
  • 兄弟组件: 消息订阅发布,集中式管理
  • 祖孙组件:消息订阅发布,集中式管理,conText

三.总结

总结了四期 总算完结了 这些就是我学习React的一些学习笔记了,日后如果还有其他的内容的话应该会深究后写成单独的文章了。

  JavaScript知识库 最新文章
ES6的相关知识点
react 函数式组件 & react其他一些总结
Vue基础超详细
前端JS也可以连点成线(Vue中运用 AntVG6)
Vue事件处理的基本使用
Vue后台项目的记录 (一)
前后端分离vue跨域,devServer配置proxy代理
TypeScript
初识vuex
vue项目安装包指令收集
上一篇文章      下一篇文章      查看所有文章
加:2021-07-31 16:32:00  更:2021-07-31 16:35:29 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年5日历 -2024/5/7 8:32:52-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码