React基础-快速梳理

React介绍

React由Meta公司开发,是一个用于构建Web和原生交互界面的库

React的优势

相较于传统基于DOM开发的优势

  1. 组件化的开发方式
  2. 不错的性能

相较于其它前端框架的优势

  1. 丰富的生态
  2. 跨平台支持

开发环境创建

create-react-app是一个快速创建React开发环境的工具,底层由Webpack构件,封装了配置细节,开箱即用

执行命令:

bash 复制代码
npx create-react-app react-basic
  1. npx - Node.js工具命令,查找并执行后续的包命令
  2. create-react-app - 核心包(固定写法),用于创建React项目
  3. react-basic React项目的名称(可以自定义)
    启动项目 npm start

渲染流程:App.js跟组件导入到index.js里,把根组件渲染到id为root的index.html

JSX基础

什么是JSX

JSX是JavaScript和XMl(HTML)的缩写,表示在JS代码中编写HTML模版结构,它是React中构建UI的方式

jsx 复制代码
const message = 'this is message'
function App(){
  return (
    <div>
      <h1>this is title</h1>
      {message}
    </div>
  )
}

优势:

  1. HTML的声明式模版写法
  2. JavaScript的可编程能力

JSX高频场景-JS表达式

在JSX中可以通过 大括号语法{} 识别JavaScript中的表达式,比如常见的变量、函数调用、方法调用等等

  1. 使用引号传递字符串

  2. 使用JS变量

  3. 函数调用和方法调用

  4. 使用JavaScript对象

注意:if语句、switch语句、变量声明不属于表达式,不能出现在{}中

jsx 复制代码
const message = 'this is message'
function getAge(){
  return 18
}
function App(){
  return (
    <div>
      <h1>this is title</h1>
      {/* 字符串识别 */}
      {'this is str'}
      {/* 变量识别 */}
      {message}
      {/* 函数调用 渲染为函数的返回值 */}
      {getAge()}
    </div>
  )
}

JSX高频场景-列表渲染

在JSX中可以使用原生js种的map方法 实现列表渲染

key的作用:React框架内部使用,提升更新性能的

jsx 复制代码
const list = [
  {id:1001, name:'Vue'},
  {id:1002, name: 'React'},
  {id:1003, name: 'Angular'}
]
function App() {
  return (
    <div className="App">
      this is App
      {/* 渲染列表 */}
      {/* map循环哪个结构,return哪个结构 */}
    <ul>
    {list.map(item => <li key={item.id}> {item.name} </li>)}
    </ul>
    </div>
  )
}

JSX高频场景-条件渲染

在React中,可以通过逻辑与运算符&&、三元表达式(?😃 实现基础的条件渲染

jsx 复制代码
const flag = true
const loading = false
function App(){
  return (
    <div>
      {flag && <span>this is span</span>}
      {loading ? <span>loading...</span>:<span>this is span</span>}
    </div>
  )
}

JSX高频场景-复杂条件渲染

解决方案:自定义函数 + 判断语句

jsx 复制代码
const type = 1  // 0|1|3

function getArticleJSX(){
  if(type === 0){
    return <div>无图模式模版</div>
  }else if(type === 1){
    return <div>单图模式模版</div>
  }else(type === 3){
    return <div>三图模式模版</div>
  }
}
function App(){
  return (
    <div>
      { getArticleJSX() }
    </div>
  )
}

React的事件绑定

基础实现

React中的事件绑定,通过语法 on + 事件名称 = { 事件处理程序 },整体上遵循驼峰命名法

jsx 复制代码
function App(){
  const clickHandler = ()=>{
    console.log('button按钮点击了')
  }
  return (
    <button onClick={clickHandler}>click me</button>
  )
}

使用事件参数

在事件回调函数中设置形参e即可

jsx 复制代码
function App(){
  const clickHandler = (e)=>{
    console.log('button按钮点击了', e)
  }
  return (
    <button onClick={clickHandler}>click me</button>
  )
}

传递自定义参数

语法:事件绑定的位置改造成箭头函数的写法,在执行clickHandler实际处理业务函数的时候传递实参

jsx 复制代码
function App(){
  const clickHandler = (name)=>{
    console.log('button按钮点击了', name)
  }
  return (
    <button onClick={()=>clickHandler('jack')}>click me</button>
  )
}

注意:不能直接写函数调用,这里事件绑定需要一个函数引用

同时传递事件对象和自定义参数

语法:在事件绑定的位置传递事件实参e和自定义参数,clickHandler中声明形参,注意顺序对应

jsx 复制代码
function App(){
  const clickHandler = (name,e)=>{
    console.log('button按钮点击了', name,e)
  }
  return (
    <button onClick={(e)=>clickHandler('jack',e)}>click me</button>
  )
}

React组件基础使用

一个组件就是一个用户界面的一部分,它可以有自己的逻辑和外观,组件之间可以互相嵌套,也可以服用多次

组件基础使用

在React中,一个组件就是首字母大写的函数,内部存放了组件的逻辑和视图UI, 渲染组件只需要把组件当成标签书写即可

jsx 复制代码
// 1. 定义组件
function Button(){
  return <button>click me</button>
}
// 2. 使用组件
function App(){
  return (
    <div>
      {/*方式1: 自闭和 */}
      <Button/>
      {/*方式2: 成对标签 */}
      <Button></Button>
    </div>
  )
}

组件状态管理-useState

基础使用

1.useState 是一个 React Hook(函数),返回值是一个数组

2.数组中第一个参数是状态变量,第二个参数是set函数用来修改状态变量

3.useState参数将作为count的初始值

和普通JS变量不同的是,状态变量一旦发生变化组件的视图UI也会跟着变化(数据驱动视图)

jsx 复制代码
import {useState} from 'react'
function App() {
  const [ count, setCount ] = useState(0)
  return (
    <div className="App">
     <button onClick={()=>setCount(count+1)}>{ count }</button>
    </div>
  );
}

状态的修改规则

在React中状态被认为是只读的,我们应该始终替换它而不是修改它, 直接修改状态不能引发视图更新

修改对象状态

对于对象类型的状态变量,应该始终给set方法一个全新的对象 来进行修改

jsx 复制代码
import {useState} from 'react'
function App() {
  const [form,setForm] = useState({name:'jack'})
  const changeForm = ()=>{
    //错误写法:直接修改 form.name = 'john'
    //正确写法
    setForm({...form,name:'apple'})
  }
  return (
    <div className="App">
     <button onClick={changeForm}>{ form.name }</button>
    </div>
  );
}
export default App;

组件的基础样式处理

React组件基础的样式控制有俩种方式,行内样式和class类名控制

jsx 复制代码
<div style={{ color:'red'}}>this is div</div>
css 复制代码
.foo{       //index.css的内容
  color: red;
}
jsx 复制代码
import './index.css'    //导入css文件
function App(){
  return (
    <div>
      <span className="foo">this is span</span>
    </div>
  )
}

B站评论案例

  1. 渲染评论列表
  2. 删除评论实现
    1. 删除显示------条件渲染
    2. 删除功能------拿到当前项id以id为条件对评论列表做filter过滤
  3. 渲染导航Tab和高亮实现
  4. 评论列表排序功能实现

完成版本

jsx 复制代码
 import { useState } from 'react'
import './App.scss'
import avatar from './images/bozai.png'
import orderBy from 'lodash/orderBy'

/**
 * 评论列表的渲染和操作
 *
 * 1. 根据状态渲染评论列表
 * 2. 删除评论
 */

// 评论列表数据
const defaultList = [
  {
    // 评论id
    rpid: 3,
    // 用户信息
    user: {
      uid: '13258165',
      avatar: '',
      uname: '周杰伦',
    },
    // 评论内容
    content: '哎哟,不错哦',
    // 评论时间
    ctime: '10-18 08:15',
    like: 88,
  },
  {
    rpid: 2,
    user: {
      uid: '36080105',
      avatar: '',
      uname: '许嵩',
    },
    content: '我寻你千百度 日出到迟暮',
    ctime: '11-13 11:29',
    like: 88,
  },
  {
    rpid: 1,
    user: {
      uid: '30009257',
      avatar,
      uname: '黑马前端',
    },
    content: '学前端就来黑马',
    ctime: '10-19 09:00',
    like: 66,
  },
]
// 当前登录用户信息
const user = {
  // 用户id
  uid: '30009257',
  // 用户头像
  avatar,
  // 用户昵称
  uname: '黑马前端',
}

/**
 * 导航 Tab 的渲染和操作
 *
 * 1. 渲染导航 Tab 和高亮
 * 2. 评论列表排序
 *  最热 => 喜欢数量降序
 *  最新 => 创建时间降序
 */

// 导航 Tab 数组
const tabs = [
  { type: 'hot', text: '最热' },
  { type: 'time', text: '最新' },
]

const App = () => {
  // 导航 Tab 高亮的状态
  const [activeTab, setActiveTab] = useState('hot')
  const [list, setList] = useState(defaultList)

  // 删除评论
  const onDelete = rpid => {
    // 如果要删除数组中的元素,需要调用 filter 方法,并且一定要调用 setList 才能更新状态
    setList(list.filter(item => item.rpid !== rpid))
  }

  // tab 高亮切换
  const onToggle = type => {
    setActiveTab(type)
    let newList
    if (type === 'time') {
      // 按照时间降序排序
      // orderBy(对谁进行排序, 按照谁来排, 顺序)
      newList = orderBy(list, 'ctime', 'desc')
    } else {
      // 按照喜欢数量降序排序
      newList = orderBy(list, 'like', 'desc')
    }
    setList(newList)
  }

  return (
    <div className="app">
      {/* 导航 Tab */}
      <div className="reply-navigation">
        <ul className="nav-bar">
          <li className="nav-title">
            <span className="nav-title-text">评论</span>
            {/* 评论数量 */}
            <span className="total-reply">{list.length}</span>
          </li>
          <li className="nav-sort">
            {/* 高亮类名: active */}
            {tabs.map(item => {
              return (
                <div
                  key={item.type}
                  className={
                    item.type === activeTab ? 'nav-item active' : 'nav-item'
                  }
                  onClick={() => onToggle(item.type)}
                >
                  {item.text}
                </div>
              )
            })}
          </li>
        </ul>
      </div>

      <div className="reply-wrap">
        {/* 发表评论 */}
        <div className="box-normal">
          {/* 当前用户头像 */}
          <div className="reply-box-avatar">
            <div className="bili-avatar">
              <img className="bili-avatar-img" src={avatar} alt="用户头像" />
            </div>
          </div>
          <div className="reply-box-wrap">
            {/* 评论框 */}
            <textarea
              className="reply-box-textarea"
              placeholder="发一条友善的评论"
            />
            {/* 发布按钮 */}
            <div className="reply-box-send">
              <div className="send-text">发布</div>
            </div>
          </div>
        </div>
        {/* 评论列表 */}
        <div className="reply-list">
          {/* 评论项 */}
          {list.map(item => {
            return (
              <div key={item.rpid} className="reply-item">
                {/* 头像 */}
                <div className="root-reply-avatar">
                  <div className="bili-avatar">
                    <img
                      className="bili-avatar-img"
                      src={item.user.avatar}
                      alt=""
                    />
                  </div>
                </div>

                <div className="content-wrap">
                  {/* 用户名 */}
                  <div className="user-info">
                    <div className="user-name">{item.user.uname}</div>
                  </div>
                  {/* 评论内容 */}
                  <div className="root-reply">
                    <span className="reply-content">{item.content}</span>
                    <div className="reply-info">
                      {/* 评论时间 */}
                      <span className="reply-time">{item.ctime}</span>
                      {/* 评论数量 */}
                      <span className="reply-time">点赞数:{item.like}</span>
                      {user.uid === item.user.uid && (
                        <span
                          className="delete-btn"
                          onClick={() => onDelete(item.rpid)}
                        >
                          删除
                        </span>
                      )}
                    </div>
                  </div>
                </div>
              </div>
            )
          })}
        </div>
      </div>
    </div>
  )
}

export default App

React表单控制

受控绑定

概念:用React组件的状态(useState)控制input表单的状态

React(state) -----> state绑定到input 的value属性 -----> input(value)

React(state)<----- 把input最新的value值设置给state <----- value

jsx 复制代码
//通过value属性绑定react状态
//绑定onchange事件,通过事件参数e拿到输入框最新的值,反向修改到react状态
function App(){
  const [value, setValue] = useState('')
  return (
    <input 
      type="text" 
      value={value} 
      onChange={e => setValue(e.target.value)}
    />
  )
}

获取DOM(非受控绑定)

  1. useRef生成ref对象,绑定到dom标签身上
  2. dom可用时,ref.current获取dom (渲染完毕之后 dom生成之后才能用)
jsx 复制代码
import { useRef } from 'react';
function App() {
  const inputRef = useRef(null)
  const showDom = ()=> {
    console.dir(inputRef.current)
  }
  return (
    <div className="App">
    <input type="text" ref={inputRef}/>   //绑定到dom标签身上
    <button onClick={showDom}> 获取DOM</button>
    </div>
  );
}
export default App;
}

时间格式修改

jsx 复制代码
npm install dayjs  //插件安装
jsx 复制代码
import dayjs from 'dayjs';
function App() {
  const showDom = ()=> {
    console.log(dayjs(new Date()).format('MM-DD hh:mm'));
  }

组件通信

父子通信-父传子

**实现步骤 **

  1. 父组件传递数据 - 在子组件标签上绑定属性
  2. 子组件接收数据 - 子组件通过props参数接收数据

props:是一个对象,里面包含了父组件传递过来的所有数据

jsx 复制代码
function Son(props){
  return <div>{ props.name }</div>
}
function App(){
  const name = 'this is app name'
  return (
    <div>
       <Son name={name}/>  //父组件中使用子组件
    </div>
  )
}

特殊的prop-chilren

场景:当我们把内容嵌套在组件的标签内部时,组件会自动在名为children的prop属性中接收该内容

jsx 复制代码
function Son(props){
  console.log(props)
  return <div> {props.children}</div>
}
function App() {]
    return (
      <div>
        <Son>
          <span>this is span</span>
        </Son>
      </div>
    )
}
export default App;

父子通信-子传父

核心思路:在子组件中调用父组件中的函数并传递实参

jsx 复制代码
function Son({ onGetMsg }){ //解构
  const sonMsg = 'this is son msg'
  return (
    <div>
      {/* 在子组件中执行父组件传递过来的函数 */}
      <button onClick={()=>onGetMsg(sonMsg)}>send</button>
    </div>
  )
}
function App(){
  const getMsg = (msg)=>console.log(msg)
  return (
    <div>
      {/* 传递父组件中的函数到子组件 */}
       <Son onGetMsg={ getMsg }/>
    </div>
  )
}

兄弟组件通信

实现思路: 借助 状态提升 机制,通过共同的父组件进行兄弟之间的数据传递

  1. A组件先通过子传父的方式把数据传递给父组件App A ---> App
  2. App拿到数据之后通过父传子的方式再传递给B组件 App ---> B
jsx 复制代码
// 1. 通过子传父 A -> App
// 2. 通过父传子 App -> B

import { useState } from "react"

function A ({ onGetAName }) {
  // Son组件中的数据
  const name = 'this is A name'
  return (
    <div>
      this is A compnent,
      <button onClick={() => onGetAName(name)}>send</button>
    </div>
  )
}

function B ({ name }) {
  return (
    <div>
      this is B compnent,
      {name}
    </div>
  )
}

function App () {
  const [name, setName] = useState('')
  const getAName = (name) => {
    setName(name)
  }
  return (
    <div>
      this is App
      <A onGetAName={getAName} />
      <B name={name} />
    </div>
  )
}

export default App

context跨层组件通信

比如一个层级关系为APP(A(B)) ,想实现APP--->B 的通信

实现步骤:

  1. 使用 createContext方法创建一个上下文对象Ctx
  2. 在顶层组件(App)中通过 Ctx.Provider 组件提供数据
  3. 在底层组件(B)中通过 useContext 钩子函数获取消费数据
jsx 复制代码
// App -> A -> B

import { createContext, useContext } from "react"

// 1. createContext方法创建一个上下文对象

const MsgContext = createContext()

function A () {
  return (
    <div>
      this is A component
      <B />
    </div>
  )
}

function B () {
  // 3. 在底层组件 通过useContext钩子函数使用数据
  const msg = useContext(MsgContext)
  return (
    <div>
      this is B compnent,{msg}
    </div>
  )
}

function App () {
  const msg = 'this is app msg'
  return (
    <div>
      {/* 2. 在顶层组件 通过Provider组件提供数据 */}
      <MsgContext.Provider value={msg}>
        this is App
        <A />
      </MsgContext.Provider>
    </div>
  )
}

export default App

React副作用管理-useEffect

useEffect是一个React Hook函数,用于在React组件中创建不是由事件引起而是由渲染本身引起的操作,比如发送AJAX请求,更改DOM等等

基础使用

需求:在组件渲染完毕之后,立刻从服务端获取平道列表数据并显示到页面中

jsx 复制代码
useEffect(() => { }  ,  [ ])

说明:

  1. 参数1是一个函数,可以把它叫做副作用函数,在函数内部可以放置要执行的操作

  2. 参数2是一个数组(可选参),依赖项数组,不同依赖项会影响第一个参数函数的执行,当是一个空数组的时候,副作用函数只会在组件渲染完毕之后执行一次

    接口地址:http://geek.itheima.net/v1_0/channels

useEffect依赖说明

useEffect副作用函数的执行时机存在多种情况,根据传入依赖项的不同,会有不同的执行表现

依赖项 副作用功函数的执行时机
没有依赖项 组件初始渲染执行一次 + 任意组件更新时执行
空数组依赖 只在初始渲染时执行一次
添加特定依赖项 组件初始渲染 + 依赖项变化时执行

清除副作用

概念:在useEffect中编写的由渲染本身引起的对接组件外部的操作,社区也经常把它叫做副作用操作,比如在useEffect中开启了一个定时器,我们想在组件卸载时把这个定时器再清理掉,这个过程就是清理副作用

说明:清除副作用的函数最常见的执行时机是在组件卸载时自动执行

jsx 复制代码
useEffect(()=> {
  //实现副作用操作逻辑
  return()=>{
    //清除副作用逻辑
  }
},[])
jsx 复制代码
//例子:在Son组件渲染时开启一个定时器,卸载时清除这个定时器
import { useEffect, useState } from "react"
function Son () {
  // 1. 渲染时开启一个定时器
  useEffect(() => {
    const timer = setInterval(() => {
      console.log('定时器执行中...')
    }, 1000)
    return () => {
      // 清除副作用(组件卸载时)
      clearInterval(timer)
    }
  }, [])
  return <div>this is son</div>
}
function App () {
  // 通过条件渲染模拟组件卸载
  const [show, setShow] = useState(true)
  return (
    <div>
      {show && <Son />} //条件渲染
      <button onClick={() => setShow(false)}>卸载Son组件</button>
    </div>
  )
}

export default App

自定义Hook实现

概念:自定义Hook是以 use打头的函数,通过自定义Hook函数可以用来实现逻辑的封装和复用

jsx 复制代码
// 封装自定义Hook

// 问题: 布尔切换的逻辑 当前组件耦合在一起的 不方便复用

// 解决思路: 自定义hook

import { useState } from "react"

function useToggle () {
  // 可复用的逻辑代码
  const [value, setValue] = useState(true)

  const toggle = () => setValue(!value)

  // 哪些状态和回调函数需要在其他组件中使用 return
  return {
    value,
    toggle
  }
}

// 封装自定义hook通用思路

// 1. 声明一个以use打头的函数
// 2. 在函数体内封装可复用的逻辑(只要是可复用的逻辑)
// 3. 把组件中用到的状态或者回调return出去(以对象或者数组)
// 4. 在哪个组件中要用到这个逻辑,就执行这个函数,解构出来状态和回调进行使用


function App () {
  const { value, toggle } = useToggle()
  return (
    <div>
      {value && <div>this is div</div>}
      <button onClick={toggle}>toggle</button>
    </div>
  )
}

export default App

React Hooks使用规则

  1. 只能在组件中或者其他自定义Hook函数中调用
  2. 只能在组件的顶层调用,不能嵌套在if、for、其它的函数中
相关推荐
敲啊敲952712 分钟前
uni-app之旅-day02-分类页面
前端·javascript·uni-app
汪子熙21 分钟前
Angular 客户端渲染时,从 ng-state 里读取 SSR 状态的具体逻辑
前端·javascript·angular.js
谢尔登1 小时前
【移动端】Viewport 视口
前端·javascript·html
哇咔咔哇咔1 小时前
【科普】什么是npm和pip?它们之间有什么异同?
前端·npm·pip
二十雨辰1 小时前
[uni-app]小兔鲜-06地址+sku+购物车
前端·javascript·vue.js·uni-app
码农0001 小时前
Vue2 + ElementUI + axios + VueRouter入门
前端·javascript·elementui
吕永强2 小时前
CSS概述
前端·css·css3
古韵2 小时前
用alovajs的use-fetcher,轻松搞定数据请求难题
前端·javascript·axios
DT——2 小时前
vue中如何实现组件通信
前端·javascript·vue.js