react学习(1)-从hooks开始

349 阅读4分钟

前言

了解过react的同学都知道,react分类组件和函数组件,本篇作为react学习的的第一篇文章,是为了记录react中函数组件的基本知识,从项目生成开始,逐步记录react语法,组件传值,hooks函数,前后端交互,路由,状态管理等等。

因为是最基础内容,项目的结构不会很复杂。我将会在下一篇文章中记录,react对比vue的话,我们要实现一个完整的项目,react的使用和vue会有哪些区别。

项目创建

首先确保本地node环境已经配置,然后打开我们的命令行工具

全局安装creact-react-app脚手架

npm i -g create-react-app 

接着我们使用creact-react-app脚手架创建项目

creact-react-app hooks-component-demo

这里的 hooks-component-demo是项目文件夹的名称,也是package.json中的项目名称。

然后我们就得到了一个最基础的react项目了

运行起来

cd hooks-component-demo

npm start

目录结构

hooks-component-demo
---node_modules
---public
    --favicon.ico
    --index.html    // 首页的模板文件
    --manifest.json // 移动端配置文件
    --robots.txt
---src
    --App.css
    --App.js
    --App.test.js
    --index.js
    --reportWebVitals.js 
    --setupTests.js
.eslintcahe
.gitignore
package.json
README.md
yarn.lock

至此我们就有了一个基本的react项目目录。由于我们主要是学习react的基本语法和声明周期这些,所以这些文件和文件中的内容暂时不展开去记录,后面抽时间了解一下每个文件的作用。

hooks函数的使用

函数式组件

要了解hooks的使用,我觉得应该先知道什么是函数式组件,以下:

1.函数式组件本质就是一个普通函数,接收一个props参数,并返回reactElement

2.函数式组件中没有this和生命周期函数,不能使用string ref

3.使用函数式组件是应该尽量减少在函数组件内部声明子函数,否则每次组件更新时,都会重新创建这个函数,导致性能比较差。

实例:src下App.js文件中

import React from 'react';

// 新建子组件
function Child(props) {
    console.log('子组件接受的props', props);
    return (
        <div> <h2> 我是子组件->{props.name} </h2> </div>
    )
}

function App() {
    return (
        <div> 
            <h1> App组件 </h1>
            <Child name={'child'} />
        </div>
    )
}

export default App

什么是react hooks

react hooks是react16.8中新增的功能,它们使得我们无需编写类即可使用状态和其他的react功能

react hooks的优势

1.简化组件的逻辑

2.复用状态的逻辑

3.无需使用类组件编写

常用的hooks

useState

在react中,通常use开头的都是hook,所以后期我们自定义hook的时候也是遵循这套规则

useState使用

[state, setState] = useState(inittate)
    --state当前对应状态,setState 修改state的方法。
useState 返回一个数组,数组第一项是初始的状态数据,数组的第二项是修改对应状态数据的方法。

import {useState,createRef} from 'react';

const inputRef = createRef()

// 新建子组件
function Child(props) {
    console.log('子组件接受的props', props);
    const {name,setName} = props;
    return (
        <div> 
            <h2> 我是子组件->{props.name} </h2> 
            <input type="text"
                value={name}
                ref={inputRef}
                onChange={({target})=>{
                    setName(target.value)
                }}
            />
        </div>
    )
}

function App() {
    let [name, setName] = useState('child')
    return (
        <div> 
            <h1> App组件 </h1>
            <Child name={name} setName={setName} />
        </div>
    )
}

export default App

多状态时,也可以传入对象,比如

const [state, setState] = useState({name: 'child',id: 1})

useEffect

useEffect相当于类组件中componentDidMount,componentDidUpdate和componentWillUnmount的一个综合体;只希望在组件挂在后执行某些事情(componentDidMount),如果我们只需要在组件挂在后执行useEffect,我们可以给一个空的数组,即:useEffect(()=>{}, [])

import {useEffect, useState} from 'react';
/**
*    useEffect: 直译为副作用
*        useEffect(callback, 数组) 接收两个参数
*        第一个参数为回调函数,回调函数内部可以有一个返回值,该返回值必须是一个函数,
*        且该返回值函数会在组件即将卸载的时候执行;第二个参数是一个可选数组,数组中有三个依赖,
*        [依赖1,依赖2,依赖3],
**/

function Child(props){
    const {name, setName, age, setAge} = props
    useEffect(()=>{
        console.log('组件挂载/更新/卸载了')
        return ()=>{
            console.log('%c 组件卸载了','color: red')
        }
        // 这里监听了name依赖,所以name改变,useEffect就会执行,无论age怎么改变,useEffect都不执行,
        // 这样可以实现具体控制单个值,来决定是否执行useEffect的逻辑,更加细粒度的控制,提高性能
    },[name]); // 如果此处依赖数组是[], 那么只会在组件挂载时执行该useEffect

    return(
        <div>
            <p> name: {name} </p>
            <input type="text" value={name} onChange={(target)=>{setName(target.value)}] />
            <br/>
            <p> age: {age} </p>
            <input type="text" value={name} onChange={(target)=>{setName(target.value)}] />
        </div>
    )
}

function App(){
    let [name, setName] = useState('xiaobai');
    let [age, setAge] = useState(18);
    let [isShow, setIsShow] = useState(true);
    return (
        <div>
            {isShow?(<Child name={name} setName={setName} age={age} setAge={setAge} />):''}
            <button onClick={()=>{setIsShow(!isShow)}} > {isShow?'隐藏':'显示'}children </button>
        </div>
    )
}

export default App

useRef

作用:获取真实Dom;记录组件更新之前的值。

使用

import {useState, useEffect, useRef} from 'react'

function Child(props){
    const {name ,setName} = props
    let [age, setAge] = useState(18)
    const div = useRef(null)
    const preVal = useRef({ // 记录两个参数
        name, age
    })

    useEffect(()=>{
        console.log('div current:', div.current)
        console.log('preVal current:', preVal.current)
        preVal.current = { // 每次组件更新后,记录新的name,age的值
            name, age
        }
    })
    return (
        <div ref={div}>
            <h1>child组件 </h1>
            <input value={name} onChange={({target})=>{setName(target.value)}} /> <span> {name} </span> <br>
            <input value={age} onChange={({target})=>{setAge(target.value)}} /> <span>{age}</span>
            </div>
    )
}

function App() {
    let [name, setName] = useState('child')
    return (
        <div>
            <Child name={name} setName={setName} />
         </div>
    )
}

export efault App

useMemo

如果我们想在组件挂载前作某些操作,可以使用useMemo

实例:

import {useMemo, useEffect, useState} from 'react'

function Child(props) {
    const {name, setName} = props
    const {age, setAge} = useState(18)
    const val = useMemo(()=>{
        console.log('组件即将挂载/即将更新')
        return `姓名&{name} 年龄&{age}`
    }, [name, age])

    usetEffect(()=>{
        console.log(组件挂载完成或更新完成)
    })
    console.log('组件挂载或更新)
    return (
        <div>
            <p>{val} </p>
            <input value={name} onChange={({target})=>{setName(target.value)}} /> <span> {name} </span
>           <input value={age} onChange={(target)=>{setAge(target.value)}} /> <span> {age} {/span}
        </div>
    )
}

function App() {
    let [name, setName] = useState('child')
    return (
        <div> 
            <h1> app组件</h1>
            <Child name={name} setName={setName} />
        </div>
    )
}

Hook使用规则

只在函数中调用hook

只在最顶层使用hook,否则会报错

未完待续。。。