React学习总结

提示:文章仅仅是用来记录学习总结,会有很多其他文章借鉴的地方,还望见谅!

文章目录

  • 前言---> React学习总结
  • 一、JSX
  • 二、react状态
    • 1.类组件中状态使用
    • 2.事件绑定 改变this的指向
    • 3.状态在函数组件中的使用
  • 三、react 类组件声明周期(钩子函数)
      • 生命周期函数很多,需要掌握6个:
    • 一、初始渲染阶段:这是组件即将开始其生命之旅并进入 DOM 的阶段。
      • 1.constructor(装载)
      • 2.render(渲染)
      • 3.componentDidMount(装载之后)
    • 二、更新阶段:一旦组件被添加到 DOM,它只有在 prop 或状态发生变化时才可能更新和重新渲染。这些只发生在这个阶段。
      • 4.render(更新)
      • 5.componentDidUpdate(更新) -- 用来做监听器
    • 三、卸载阶段:这是组件生命周期的最后阶段,组件被销毁并从 DOM 中删除。
      • 6.componentWillUnmount(卸载)
      • 7.shouldComponentUpdate(了解)
  • 四、状态提升(就是子父组件通信)
  • 五、Hooks (常用)
    • 1.Hooks 含义
    • 2.Hooks的用法
      • useState():状态钩子
      • useEffect():副作用钩子
      • useCallback():记忆函数
      • useMemo():记忆组件
      • useRef():保存引用值
      • useContext():共享状态钩子(上下文)
      • useReducer():Action钩子
    • 创建自己的Hooks
  • 总结


前言—> React学习总结

React,用于构建用户界面的 JavaScript 库,提供了 UI 层面的解决方案,遵循组件设计模式、声明式编程范式和函数式编程概念,以使前端应用程序更高效,使用虚拟DOM来有效地操作DOM,遵循从高阶组件到低阶组件的单向数据流,帮助我们将界面成了各个独立的小块,每一个块就是组件,这些组件之间可以组合、嵌套,构成整体页面。


提示:以下是本篇文章正文内容,下面案例可供参考

一、JSX

JSX 是JavaScript XML的缩写,不是html或xml,基于ECMAScript的一种新特性,一种定义带属性树结构的语法;

  • jsx元素写法 jsx形式、React.createElement的形式
  • jsx元素本质是一个对象,这个对象是不可变的对象—因为jsx元素最终需要被渲染成真实的DOM元素 所以不能去修改 只能去使用
  • 由class或function定义出来的叫组件,组件名必须大写
  • 由jsx语法或React.createElement返回的结果叫jsx元素,jsx元素充当组件的视图模板
  • 在jsx元素中,你要嵌套表达式,必须使用{}包起来,也就是说,在jsx元素中,看到{}你要知道里面放的都是表达式。任何有值的内容都是表达式。
  • jsx也可以做了函数的入参,也就是在调用一个函数时,传递传递一个jsx元素,也可以做为函数的返回值,还可以用在if或for中。
  • class 变成了 className、 for 变成了 htmlFor、tabindex 变成了tabIndex
  • jsx元素中 新增了的属性 key 用于列表渲染、ref 方便DOM操作、dangerouslySetInnerHTML 用来渲染一片html字符串
  • jsx只能有一个根标签
  • 在jsx元素,不仅组件可以使用单标签,任何html标签都可以使用单标签
  • JSX语法中是支持点语法的 React.Component React.Fragment Ml.Button Ml.Form
  • 对于组件来说,props就是自定义属性,对于html标签来说,props是我们之前学习过的html标签的属性,props和vue一样,只能使用它,不要去修改它。
  • 在使用组件时,如果组件使用双标签,在双标签之间就可以写内容,在双标签之间写的内容是通过props.children来接收的,props.children可以是任意类型的数据,基本数据类型可以,引用数据类型也可以,也可以是函数。
  • 在使用JSX时,如果是数组,可以直接渲染
  • 如果数组中有boolean值,有null,有und,会被直接忽略,也就是说不会生成对应的文本节点。

二、react状态

1.类组件中状态使用

  • 在类组件中,不推荐直接修改状态,因为修改状态的目的还需要让模板刷新, 有个专属API setState是专门来修改状态的
  • this.setState是异步的,在React18中,永远是异步的,在React18之前,有可能是同步,有可能是异步
  • this.setState({}, callback) 在修改状态时,当新值与旧值无关时,推荐使用这种写法,callback表示当状态修改后,自动执行,当状态修完后,有一些业务逻辑放到callback中。
  • this.setState((state, props)=>({}), callback) 当新值与旧值有关时,新值由旧值计算而来,形参state永远表示旧值,建议使用这种写法。callback表示当状态修改后,自动执行,当状态修完后,有一些业务逻辑放到callback中。

2.事件绑定 改变this的指向

  • 利用ES5中的bind手动绑定this
  • 利用ES6的箭头函数自动绑定this

3.状态在函数组件中的使用

函数组件中是不能定义状态,但是配合hook可以定义状态,hook类似于vue3中的组合式API,在React16.8中就有了

  • 在V18之前,在合成事件中是异步的,在宏任务和promise.then中是同步的。
  • 在V18中,无论在哪里都是异步的。

三、react 类组件声明周期(钩子函数)

只有类组件才会有生命周期钩子函数,函数式组件没有生命周期钩子的函数的。

生命周期函数很多,需要掌握6个:

装载阶段(3个):constructor, render, componentDidMount
更新阶段(2个):render,componentDidUpdate
卸载阶段(1个):componentWillUnmount

React学习总结_第1张图片

一、初始渲染阶段:这是组件即将开始其生命之旅并进入 DOM 的阶段。

1.constructor(装载)

constructor中,不能调用接口,不能进行DOM操作,不能开定时器… 一切和业务相关的代码都不能写在constructor`钩子函数中。

  • constructor中,可以做什么:

    定义状态
    绑定方法的this

2.render(渲染)

作用:用于返回组件的视图结构 , 这个结构在vue中叫模板 在react中叫jsx。它在背后生成一颗Fiber树 在vue中叫虚拟DOM在React叫Fiber树,早期的React也是使用的虚拟DOM。

render是一个函数,这个函数返回一个jsxjsxjsx元素,本质是一个对象,创建jsx元素有2种方法,一种是直接使用jsx,另外一种是React.creeateEleement。也就说,调用render函数,会生成棵Fiber树,类似于Vue中的虚拟DOM,这个Fiber树是双向链表结构。生成这课Fiber树的过程是异步的,生成的过程是可以中断,能不能中断是看浏览器忙不忙,如果浏览器比较忙,就可能中断,等待浏览器不忙时,会继续生成,直到Fiber树创建完成。然后会进行协调运算,这个协调运算类似于Vue中的Diff运算,也就是一棵老的Fiber树和一棵新的Fiber树进行对比运算。运算对比后,就会进入到commmit提交阶段一次性提交Fiber更新DOM

3.componentDidMount(装载之后)

类似于vue中的mounted,表示页面第一次渲染完成。

  • 调用接口
  • 开定时器
  • DOM操作
  • 编写业务逻辑

二、更新阶段:一旦组件被添加到 DOM,它只有在 prop 或状态发生变化时才可能更新和重新渲染。这些只发生在这个阶段。

4.render(更新)

render:组件重新渲染

5.componentDidUpdate(更新) – 用来做监听器

相当于Vue中的updated,表示页面再次渲染成功。 最好不要在componentDidUpdate内调用setState 没有出口会爆栈

监听器是用来监听数据是否变化,updated表示数据变化了,会执行updated,也就是说数据变化了,在updated也可以监听到了。要监听数据变化,在vue中,使用监听器比使用updated更方便。在react中是没有监听器的概念的,在React中实现类似于Vue的监听器的功能,需要使用compoentDidUpdate钩子函数了。
this.setState({}/fn, callback) 利用callback也可以感知到数据变化了。推荐使用componentDidUpdate。因为多个setState会合并,合并后,callback也会出问题`

三、卸载阶段:这是组件生命周期的最后阶段,组件被销毁并从 DOM 中删除。

6.componentWillUnmount(卸载)

类似于Vue中的beforeDestroy,表示组件即将销毁。在这里我们可以:

清缓存
清除定时器
关闭长连接
销毁DOM元素

7.shouldComponentUpdate(了解)

它是控制更新阶段的开关,说白了,就是来控制是否更新,当返回true就正常更新,当返回false时就不更新。
shouldComponentUpdate,返回true时,正常更新,返回false时,不执行更新阶段。注意细:

当执行forceUpdate时,会绕过shouldComponentUpdate方法,一定会进入到更新阶段。
shouldComponentUpdate,可以使用PureCompoentf替代

四、状态提升(就是子父组件通信)

  • 一个父组件,下面有两个子组件,这两个子组件,需要通信,通常会把这个状态定义到父组件中,通过父传子,再通过子传父,实现兄弟之间的通信,状态提供到父上面,就是所谓的状态提升。
  • 在React中,一般不讨论自定义属性或自定义事件,写在组件上的都叫propsprops的值可以是基本数据,也可以是对象,也可以是数组,当然也可以是函数,也可以是jsx
  • 如果是函数,函数分两类,一类叫事件函数,另一类叫渲染函数。如果是事件函数,建议以on打头,如果是渲染函数,建议不要使用on打头
  • 所谓的状态提升,就是把一个状态提升到父组件中,实现父传子,实现子传父
import { Component, useState } from "react"
class Fahrenheit extends Component {
    render() {
        const { value, onChange } = this.props
        let change = (e) => {
            // 得到最新的华氏温度,需要回传给父组件
            // console.log(e.target.value);
            // 把华氏温度变成摄氏温度,回传给父组件
            let temper = (Number(e.target.value) - 32) * 5 / 9;
            onChange(Number(temper))
        }
        return (
            <div>
                <label>华氏温度:</label>
                <input type="text" value={value * 9 / 5 + 32} onChange={change} />
            </div>
        )
    }
}
function Celsius(props) {
    let { value, onChange } = props;
    let change = (e) => {
        onChange(e.target.value);
    }
    return (
        <div>
            <label>摄氏温度:</label>
            <input type="text" value={value} onChange={change} />
        </div>
    )
}
function DemoA() {
    const [temper, setTemper] = useState(0);
    return (
        <div>
            <Fahrenheit value={temper} onChange={val => setTemper(val)}></Fahrenheit>
            <hr />
            <Celsius value={temper} onChange={val => setTemper(val)}></Celsius>
        </div>
    )
}

export default DemoA

五、Hooks (常用)

  • React的组件创建方式,一种是类组件,一种是纯函数组件,并且React团队希望,组件不要变成复杂的容器,最好只是数据流的管道。开发者根据需要,组合管道即可。也就是说组件的最佳写法应该是函数,而不是类。

函数组件和类组件的区别:
(1)函数组件没有状态(state),类组件有
(2)函数组件没有生命周期,类组件有(挂载、更新、销毁)
(3)函数组件没有this,类组件有
(4)函数组件更适合做UI展示,类组件更适合做复杂的业务逻辑组件

这就注定,函数组件更适合做UI展示的功能,涉及到状态的管理与切换,我们不得不用类组件或者redux。但我们知道类组件的也是有缺点的,比如,遇到简单的页面,代码会显得很重,并且每创建一个类组件,都要去继承一个React实例;至于Redux,更不用多说,很久之前Redux的作者就说过,“能用React解决的问题就不用Redux”。

1.Hooks 含义

“Hooks”的单词意思为“钩子”。React Hooks 的意思是,组件尽量写成纯函数,如果需要外部功能和副作用,就用钩子把外部代码“钩”进来。而React Hooks 就是我们所说的“钩子”。

2.Hooks的用法

useState():状态钩子

纯函数组件没有状态,useState()用于设置和使用组件的状态属性。语法如下:

const [state, setState] = useState(initialValue);
// state:初始的状态属性,指向状态当前值,类似this.state
// setState:修改状态属性值的函数,用来更新状态,类似setState
// initialValue:状态的初始值,该值会赋给state

注意:setState的命名为:set+State(初始状态名),并且采用小驼峰命名法。例如[count, setCount][name, setName]

示例:使用Hooks重写计数器

const Count = () => {
    const [count, setCount] = useState(0); // 将0设置为count的初始值
    const addCount = () => {
        let newCount = count;
        setCount(newCount += 1);
    }
    return (
        <div>
            <p>{count}</p>
            <button onClick={addCount}>1</button>
        </div>
    )
}

用函数组件实现了一个功能完全一样的计数器,代码看起来更加的轻便简洁,没有了继承,没有了渲染逻辑,没有了生命周期等。这就是hooks存在的意义。

useEffect():副作用钩子

useEffect()是副作用的钩子,可以实现特定的功能,如异步请求。语法如下:

useEffect(() => {
    // 回调函数,其中是要进行的异步操作代码
}, [array])
// [array]:useEffect执行的依赖,当该数组的值发生改变时,回调函数中的代码就会被指向
// 如果[array]省略,则表示不依赖,在每次渲染时回调函数都会执行
// 如果[array]是空数组,即useEffect第二项为[],表示只执行一次

示例:通过useEffect()模拟异步加载数据(第二项省略)。

const AsyncPage = () => {
    // 首先设置loading状态为true
    const [loading, setLoading] = useState(true);
    useEffect(() => {
        // 2秒后将loading状态设置为false
        setTimeout(() => {
            setLoading(false);
        }, 2000);
    })
    return (
        // 判断loading是否为true,是就显示loading,不是就显示异步请求完成
        loading ? <p>loading...</p> : <p>异步请求完成</p>
    )
}

示例:useEffect()依赖第二项数组变化

const AsyncPage = ({name}) => {
    const [loading, setLoading] = useState(true); // 设置loading状态为true
    const [person, setPerson] = useState({}); // 设置person状态为空对象
    
    useEffect(() => {
        // 首先设置loading为true,2秒后改为false,name改成传过来的参数
        setLoading(true);
        setTimeout(() => {
            setLoading(false);
            setPerson({name});
        }, 2000);
    }, [name]); // 表示当name修改才会执行回调函数
    return (
        <>
            {loading ? <p>Loading...</p> : <p>{person.name}</p>}
        </>
    )
}

const PersonPage = () => {
    // 设置初始state为空字符串
    const [state, setState] = useState("");
    const changeName = (name) => { // 修改name的函数
        setState(name);
    }
    return (
        <>
            {/*首先将state传给name*/}
            <AsyncPage name={state}/>
            <button onClick={() => { // 点击按钮后将张三传给name
                changeName("张三")
            }}>张三
            </button>
            <button onClick={() => {
                changeName("李四")
            }}>李四
            </button>
        </>
    )
}

useEffect和useLayoutEffect的区别

useEffect()useLayoutEffect()主要的区别是调用时机不同。

useLayoutEffect()componentDidMount()componentDidUpate()一致,在react完成DOM更新后马上同步调用代码,它会阻塞页面的渲染,而useEffect()则会在页面渲染完后才会异步调用。

在实际使用中如果项避免页面抖动,可以把需要操作DOM的代码放在useLayoutEffect()中,在该函数中做DOM操作,这些DOM修改会和react做出的更改一起被一次性渲染到屏幕上,只有一次回流重绘的代价。

useCallback():记忆函数

useCallback()为记忆函数,它可以防止因为组件重新渲染,导致方法被重新创建,起到缓存作用。语法如下:

useCallback(() => {
    // 回调函数,当array改变后,该函数才会重新声明
}, [array])
// 如果[array]为空数组,那么就是第一次创建后就被缓存,如果后期array改变了,拿到的还是老的array
// 如果不传入第二个参数,每次都会重新声明一次,拿到的就是最新的array

比如说下面一段代码中,我们可以看到有很多的函数,当我们在return中修改一个状态,就会导致整个页面重新渲染,那么这些函数(handleChange1handleChange2…)也会被重新创建,这样会造成性能的浪费,因此可以使用useCallback将这些函数缓存下来,这样下一次页面重新渲染的时候,某些函数就不会重新被创建了。

        const UseCallback = function () {
            const handleChange1 = () => {
                // 具体代码
            }
            const handleChange2 = () => {
                // 具体代码
            }
            const handleChange3 = () => {
                // 具体代码
            }
            const handleChange4 = () => {
                // 具体代码
            }
            return (
                <div>
                    {/*具体代码*/}
                </div>
            )
        }

使用useCallback()时,只需要将其写在整个函数外部即可,上面代码使用useCallback()后效果如下,每当依赖项改变时,该函数才会被重新创建,如果依赖项不变,则不会重新创建。

        const UseCallback = function () {
            const handleChange1 = useCallback(
                () => {
                    // 具体代码
                }, [依赖项]
            )
            const handleChange2 = useCallback(
                () => {
                    // 具体代码
                }, [依赖项]
            )
            const handleChange3 = useCallback(
                () => {
                    // 具体代码
                }, [依赖项]
            )
            const handleChange4 = useCallback(
                () => {
                    // 具体代码
                }, [依赖项]
            )
            return (
                <div>
                    {/*具体代码*/}
                </div>
            )
        }

useMemo():记忆组件

useCallback()的功能可以由useMemo()所替代,useMemo()也可以返回一个记忆函数,语法如下:

useMemo(() => fn, [])
// useCallback(fn, []) = useMemo(() => fn, [])

useCallback()useMemo()的区别:

useCallback()不会执行第一个参数函数,而是将其返回,useMemo()会执行第一个函数并且将函数执行结果返回给你。useCallback()常用记忆时间函数,生成记忆后的时间函数传递给子组件使用,useMemo()更适合经过函数计算得到一个确定的值,比如记忆组件。

useRef():保存引用值

useRef()等价于类组件中的React.createRef(),语法如下:

const loginRef = useRef();

使用useRef()创建了一个值后,就可以将其绑定到DOM节点上,给DOM节点增加一个ref属性,将loginRef传入,则可以通过其current属性获取到DOM节点的值,语法如下:

<input ref={loginRef}/>

除此之外,我们都知道useState()可以保存一个状态,那么另一个方法就是使用useRef(),为useRef()传入一个初始值,它可以帮助我们记住这个状态。

useContext():共享状态钩子(上下文)

useContext()可以共享状态,作用是进行状态的分发(React16.x以后的版本支持),避免了使用Props进行数据的传递。语法如下:

// 第一步:创建全局的Context
const AppContext = React.createContext([初始化参数])

// 第二步:通过全局的Context进行状态值的共享
<AppContext.Provider value={{ 属性名:}}>
    <组件1 />
    <组件2 />
</AppContext>

示例:A组件和B组件共享一个状态

const Count = () => {
    const AppContext = React.createContext({});
    const A = () => {
        const {name} = useContext(AppContext);
        return (
            <div>
                我是A组件,我的名字是:{name}
            </div>
        )
    }
    const B = () => {
        const {name} = useContext(AppContext);
        return (
            <div>
                我是B组件,我的名字是:{name}
            </div>
        )
    }
    return (
        <AppContext.Provider value={{name: "橘猫吃不胖"}}>
            <A/>
            <B/>
        </AppContext.Provider>
    )
}

useReducer():Action钩子

在使用React的过程中,如遇到状态管理,一般会用到Redux,而React本身是不提供状态管理的。而useReducer()提供了状态管理。

useReducer()useState()的替代方案。首先,关于redux我们都知道,其原理是通过用户在页面中发起action,从而通过reducer方法来改变state,从而实现页面和状态的通信。而Reducer的形式是(state, action) => newstate,返回当前的 state 以及与其配套的 dispatch 方法。。Hooks的useReducer()是这样的:

const [state, dispatch] = useReducer(reducer, initialState)

它接受reducer函数和状态的初始值作为参数,返回一个数组,其中第一项为当前的状态值,第二项为发送action的dispatch函数。

在某些场景下,useReducer 会比 useState 更适用,例如 state 逻辑较复杂且包含多个子值,或者下一个 state 依赖于之前的 state 等。并且,使用 useReducer 还能给那些会触发深更新的组件做性能优化,因为可以向子组件传递 dispatch 而不是回调函数。

例如:使用useReducer()实现一个计数器

const Count = () => {
    const reducer = (state, action) => {
        if (action.type == "add") {
            return {
                ...state,
                count: state.count + 1
            }
        } else {
            return state
        }
    }
    const addCount = () => {
        dispatch({
            type: "add"
        })
    }
    const [state, dispatch] = useReducer(reducer, {count: 0});
    return (
        <>
            <p>{state.count}</p>
            <button onClick={addCount}>1</button>
        </>
    )
}

通过代码可以看到,使用useReducer()代替了Redux的功能,但useReducer无法提供中间件等功能,假如有这些需求,还是需要用到redux。

创建自己的Hooks

以上介绍了七种最常用的react提供给我们的默认React Hooks,有时候我们需要创建我们自己想要的Hooks,来满足更便捷的开发,就是根据业务场景对以上七种Hooks进行组装,从而得到满足自己需求的钩子。

用户自定义的Hooks:
A、命名的要求:用use开头,后跟名称(首字母大写)
B、作用:根据具体业务的需求,对Hooks中默认的钩子函数进行封装,使代码的结构更加清晰,便于使用和维护

示例:封装自己的Hooks

// 封装自己的Hooks
const usePerson = ({name}) => {
    const [loading, setLoading] = useState(true);
    const [person, setPerson] = useState({});
    useEffect(() => {
        setLoading(true);
        setTimeout(() => {
            setLoading(false)
            setPerson({name})
        }, 2000)
    }, [name]) //第二个参数为默认

    return [loading, person];
}

const AsyncPage = (name) => { // 只进行UI展示
    const [loading, person] = usePerson(name)
    return (
        <>
            {loading ? <p>Loading...</p> : <p>{person.name}</p>}
        </>
    )
}

const PersonPage = () => {
    const [userName, setUserName] = useState('')

    const changeName = (name) => {
        setUserName(name);
    }
    return (
        <>
            <AsyncPage name={userName}/>
            <br/>
            <button onClick={() => {
                changeName('张三')
            }}>张三
            </button>
            <br/>
            <button onClick={() => {
                changeName('李四')
            }}>李四
            </button>
        </>
    )
}

上面代码中,将之前的例子封装成了自己的Hooks,便于共享。其中,usePerson()为自定义Hooks,它接受一个字符串,返回一个数组,数组中包括两个数据的状态,之后在使用usePerson()时,会根据传入的参数不同而返回不同的状态,然后很简便的应用于我们的页面中。


总结

React 是 Facebook 开源的,现在它是最流行的前端框架大厂必备,React 本身是一个构建 UI 的库,如果需要开发一个完整的 web 应用需要配合 react-router,redux,…

此次总结借鉴了 很多大佬文档及React官方文档,初学编程总结不完善,有误还请不吝指教!!!
React 官方中文文档

你可能感兴趣的:(react.js,学习,javascript)