React学习笔记

jsx语法

  1. 遇到{ } 就把里面的代码当js解析
  2. 遇到< > 就把里面的代码当html解析

声明组件

  1. 组件使用class声明函数组件,并且暴露出去
  2. 组件首字母必须大写(否则会报错)
  3. 声明组件使用React.Component方法
  4. 组件内部一定要有render(){}
  5. render()内部写 return ()
  6. 组件只能渲染一个根节点标签,就是所有的html要用一个标签包裹着

this指向问题

  1. react内置的方法this指向实例对象,比如state render
  2. 自定义的方法this为undinfied,但是写在组件内部,可以用箭头函数,this就会指向组件了,组件this是实例对象

三大对象

  1. state: 定义初始化数据,组件内部可以通过this.state.xx拿到
  2. props:可以父组件拿到传递过来数据,通过this.props.xx拿到
  3. ref:

组件间传值

  • 父→子:
    1. 引入子组件并且应用上
    2. 把要传递的数据以标签属性的方式传递(注意jsx语法)
    3. 子组件中可以在this.props上拿到父组件传递过来的数据
  • 子→父:
    1. 在父组件中声明一个方法,内写好修改数据的逻辑(回调函数形式可以接受传递过来的数据,然后做修改)
    2. 在标签上以标签属性的方式传递给子组件
    3. 子组件中用一个方法去触发props上传递过来的父组件中的方法
  • 深层嵌套:
    redux

react-router

  1. 下包 npx react-router-dom
  2. 引入
 
  1. 使用
  2. 说明
  • HashRouter: 锚点链接方式 路径后面会有#
  • BowserRouter:h5新特性 history.push的方式 但上线后可能会有Bug,需要后台做重定向处理
  1. 参数
  • params: 路由传参的参数可以在props.match中去拿 具体可以打印查看
  • query: props.location 中可以拿到

生命周期

  • componentWillMount 组件将要更新调用 在render之前
  • componentDidMoun 组件更新之后调用 在render之后
  • shouldComponentUpdate 返回布尔值决定能不能让页面发生更新,如果是true就是允许,false就是不允许不做任何改变,默认是true
  • componentWillUpdate 组件将要更新渲染前触发
    这个生命周期可以接受两个参数()
  • componentDidUpdate 组件将更新渲染后触发
  • componentWillReceiveProps 组件将要接受props传递的属性时触发,触发后再去触发shouldComponentUpdate 判断页面是否发生改变
  • componentWillUnmount 组件卸载前执行

setState

  • setState是用来改变state中状态的一个方法,可以传两个值,这个方法在可控时是异步的,不可控时是同步的。
    1. 对象类型 key:value 改变state中的数据
    2. callback 回调函数,当状态修改后触发,可以监听状态修改成功

条件渲染

  1. 定义数据状态
  2. 根据数据状态的不同来决定渲染什么代码
  3. 要配合jsx语法写

列表渲染

  1. 在jsx语法{ }中直接使用js的方法遍历生产html标签

key的作用

  • key在列表渲染中很重要,在新增dom节点的时候按说是修改了state触发了render()函数重新渲染,但是打开element调试面板可以看到,只是要改变的的dom节点发生了改变,其他的没有改变,这个就是根据key这个唯一索引进行的渲染操作,只要key没有发生变化,dom就不会重新渲染,render只渲染key发生改变的那一部分。可以称为是,如果数据索引没有发生变化,只重绘发生变化的部分。这样达到节省资源节省性能消耗的目的。

表单

  • 受控组件(表单双向数据绑定)
    概念:通过控制组件内部的state状态进行管理
  • 非受控组件
    概念:通过操控dom来进行改变

请求交互

  • fetch

跨域处理

  • http-proxy-middleware插件 具体使用方法看文档(类似vue-cli中的proxy)

redux & react-redux

  • redux:
    • store: 状态仓库
      用createStore方法创建,这个方法需要传入reducer作为参数 , 这个方法从redux包中取
    • reducer:状态机,action中的type行为判断应该对数据做什么操作,并且返回操作结果
    • actions : 行为,自定义 唯一的,调用dispatch时传入,去触发reducer状态机
    • store.getState() 当数据发生变化时就会调用并返回数据的值
    • store.subscribe() 监听数据变化,数据发生变化调用
  • react-redux
    需要先下载redux包,因为依赖redux,核心Provider connect 都从react-redux包中取出
    • Provider : 包裹标签,用法是在入口js中引入包裹住虚拟dom根标签,然后通过标签属性传值的方式把store传递到组件中去
ReactDOM.render(
  
    
      
    
  ,
  document.getElementById('root')
);
  • connect
    这是一个高阶函数:就是一个函数的返回值是另一个函数
    • mapStateToProps:使用时创建mapStateProps方法传入参数store用来获取store中的值
    • mapDispatchToProps:使用时创建mapDispatchToProps方法传入参数dispatch,在方法中调用dispatch触发reducer更新数据
  • 读取数据
    组建中读取数据直接通过props就可以了,因为react-redux做了包装,把数据和方法传入组件的时候包装到了props中

第三方中间件

  • 第三发中间件都需要用applyMiddleware()方法包裹,这个方法从redux中取出
  • redux-logger 打印redux每一步执行的行为信息
  • redux-thunk

合并reducer和合并action

  • 在真实项目中一定有很多action行为,也会有很多的reduce状态机去处理action,这时就需要合并统一暴露了,不然一个个引入会累死
  • 合并action : bindActionCreators 这个方法从redux包中来,使用方法如下
1 // 把actions都引入到一起
import * as countActions from './store/actions'

2 // 传入actions 和disapatch  但只有当actions是方法的时候 才会成为counter的属性
const mapDispatchToProps = (dispatch) => {
  return {
    counter:bindActionCreators(countActions,dispatch)
  }
}

3 //调用  
this.props.counter.xxx()

  • 合并reducer:combineReducers 这个方法从redux包中来,使用方法如下
1// 取出这个方法
import {combineReducers} from 'redux'

2// 把所有的reducer都引入到index.js中,因为要在这里生成store 通过provider传给组件
import {count,user} from './store/reducers'

3// 合并reducers创建
//合并reducer 为一个对象 然后整体暴露出去
export const reducers = combineReducers({
  count,
  user
})

4// 创建store
const store = createStore(reducers)

5// 读取数据
 读取数据时需要需要state.xxx  因为合并的时候相当于把数据都包装到一个对象中了
const mapStateToProps = (state) => {
  return {
    count:state.count,
    user:state.user
  }
}

调试

chrome
1 安装依赖 安装插件
chrome插件 redux-devtool
依赖 redux-devtools-extension

首屏优化之组件&路由懒加载

  • 直接贴链接了我就
  • 1 路由懒加载的实现方案及原理 https://blog.csdn.net/weixin_34244102/article/details/94067452
  • 2 使用react.lazy()方法实现路由和组件的懒加载 (适合组件式路由配置)
    https://blog.csdn.net/qq_22305897/article/details/103541634
  • 3 自定义方法实现路由懒加载 (适合路由集中式配置)
    https://blog.csdn.net/weixin_43674509/article/details/92764687
  • 4 路由集中式配置
    https://blog.csdn.net/roamingcode/article/details/95235079

hook

useState: 用来创建状态和改变状态的方法

/*
count值为10
setCount是改变count的方法,不再用setState去改变状态
*/
//创建简单数据类型数据
const [count,setCount] = useState(10)
//创建复杂数据类型数据
const [obj,setObj] = useState({
  name:'小米',
  age:18
})

useEffect: 相当于类组件中的什么周期,

它代替了componentDidMount,componentDidUpdate,componentWillUnmount
也就是说可以在这个方法中做 发请求,监听更改数据后变化重新渲染,和解绑dom事件,清除定时器,和清除网络状态

componentDidMount: 初始化发请求
componentDidUpdate: 数据变化后重新渲染操作监听
componentWillUnmount: 解绑dom事件,清除定时器,和清除网络状态(这些操作我的另一篇文章中又提起 

react中componentWillUnmount中可以做的事:https://www.jianshu.com/p/ea3cfc0a85da

useEffect 写法

//1 useEffect中要写一个回调函数
//2 可以写多个useEffect方法  每一个中做每一个生命周期中做的事以此来区分业务逻辑
useEffect(() => {
//如果直接这样写 会一直执行  页面初始化和数据更新重新渲染时都会调用这个方法
})

useEffect(() => {

  //如果第二个参数传入一个数组这样写,这个方法就是componentDidMount,在页面渲染完后执行
  //第二个参数是一个数组,当数组的每一项都没有发生变化,useEffect就不会重复执行,所以如果只想页面初始化调用一次的话就传一个空数组
  //可以在这里面绑定一些dom事件  比如 window.addEventLister('resize',callback,boolean)
  window.addEventLister('resize',callback,boolean)
  
  //开启定时器
  var time = setInterval(() => {},1000)


// 如果retrun一个函数的话 这个函数的作用就是页面卸载前触发的,相当于componentWillUnmount
// 可以做一些收尾工作,比如解绑dom事件,清除定时器  
return () => {
  //解绑Dom
 window.removeEventLister('resize',callback,boolean)
  
  //清除定时器
  clearInterval(time)
}
},[])

// 3 副作用
useEffect(() => {
    //mount后绑定的dom时间 如果该dom会被替换或者删除 绑定的事件就会失效,所以我们不能只再mount后绑定一次,要重复绑定和解绑
    document.getElementById('size').addEventListener('click',Click,false)
    return () => {
    document.getElementById('size').removeEventListener('click',Click,false)
      
    }
  })


useMemo: 用传入的数组来判断函数是否从新执行计算

-用来性能优化,传入一个数组,数组中数据变化或为true的时候执行,在渲染过程中调用,有返回值可以参与后面逻辑渲染,有点类似vue计算属性。

import React, { useState, useMemo } from 'react'
 
const Counter = (props) => {

  return (
    
//父组件传子组件 入参接受 子组件使用 {props.count}
) } const App = () => { const [count, setCount] = useState(0) // 两个参数 第一个参数是要执行的功能函数, 第二个参数[] 只传空数组就只执行一次 // useMemo 在渲染期间完成 而且有返回值 返回值可以参与渲染的,用法和useEffect一样 let double = useMemo(() => { console.log('useMemo') return count * 2 }, [count === 3]) return (

) } export default App

useCallback

  • 算是useMemo简写
 useMemo(() => fn)
 useCallback(fn)
如果useMemo返回的是一个回到函数,使用useCallBack就可以省略掉外层包裹函数

useCallback 小例子 附说明

import React, { useCallback,useState } from 'react';

const CB = () => {
  const [count,setCount] = useState(0)
  const [count1,setCount1] = useState(0)

  /**
   * useCallback 传两个参数
   * 1 回调函数
   * 2 数组  数组中是另一个值  用来决定让不让第一个值的那个回调函数执行
   * 3 如果 第二个参数中的值发生了变化则允许第一个参数执行,否则不允许第一个参数执行
   * 4 这样可以避免一些不必要的执行 达到性能优化
   * 
   * 
   * 当第一次执行useCallback中第一个参数这个回调时,不会受第二个参数影响,
   * 但之后的每一次触发都先去判断第二个参数中的值有没有发生变化,如果有就让第一个参数执行,
   * 如果没有就不让第一个参数执行
   */

  return(
    

{count}

{count1}

) } export default CB
  • useRef
    • 简介:
      1.用来获取dom
    • 用途:
      1. 获取输入框输入的值
      2. 通过输入框的值修改别的内容 (直接在获取的dom的回调函数中修改)
      //如果获取的dom是个Input那么 实例.current.value就是输入框的值
      const inputEl = useRef(null)
    
    
    
    
    
    1. 判断是否页面是初次加载还是重新渲染
    const Ref = () => {
    let didMountRef =  useRef(false)
    
    useEffect(() => {
    
      if(didMountRef.current){
        // 页面已经加载过 现在是重绘
        
      }else{
        //页面第一次加载
    
        didMountRef.current = true
      }
    
      })
    }
    
    
    
  • useContenx 用来父子组件传值
    1 先创建实例 createContext
    2 实例标签包裹子组件 并传入数据
    3 在子组件中 使用useContext(实例) 传入创建的实例 就可以拿到数据 如下
import React, { useState, createContext, useContext } from 'react'
//第一步创建context实例
const MyContext = createContext()

//父组件
const Context = () => {
  const count = useState(22)


  return (
    

这里是context测试

) } const Child = () => { const [state, dispatch] = useContext(MyContext) console.log(state) return (
{state}
) }

useContext + useReducer 实现数据共享

  • 可以传入reducer状态机和初始状态通过触发action改变数据。配合useContext可以替代redux
  
import React, { useState, createContext, useContext, useReducer } from 'react'

/**
 * 核心思想:
 *  通过 createContext创建的context对象包裹子组件,并把用useReducer创建出来的dispatch传递
 *  给子组件树。在子组件中分发action触发reducer状态机函数改变数据。
 * 
 */
const initstate = 0

const reducer = (state, action) => {
  console.log(state)
  console.log(action)
  switch (action.type) {
    case 'add':
      return state + 1
    case 'reduce':
      return state - 1
    case 'add5':
      return state + action.data
    case 'reduce5':
      return state - action.data
    default:
      return state
  }
}

// 1 创建context对象
const MyContext = createContext(null)

//子组件
const Child = (props) => {
  let dispatch = useContext(MyContext)

  return (
    

{props.count} {/* state */}

) } // 父组件 export default () => { const [state, dispatch] = useReducer(reducer, initstate) return (
hello word {/* 父组件用context对象标签 包裹子组件树 并把dispatch传递给子组件 子组件中去分发action触发reducetion 改变数据 */}
) }

自定义hook

1.定义一个函数,可以使用hook的提供的api,return直接返回一个可用的数据 在主组件中使用。可以把重复的逻辑提取到这个hook中。这个hook中执行逻辑返回状态。组件中直接使用就好了。比如发请求什么的。把url和data传进去。返回出请求结果。等等操作
2.名字必须是use开头

import React,{useState,useEffect} from 'react';


const useMousePosition = () => {
  const [positions,setPositions] = useState({x:0,y:0})

  useEffect(() => {

    const updateMouse = (e:MouseEvent) => {
      console.log('inner');
      setPositions({x:e.clientX,y:e.clientY})
    }
    document.addEventListener('mousemove',updateMouse)

    return () => {
      document.removeEventListener('mousemove',updateMouse)
    }

  })

  return positions
}


export default useMousePosition


你可能感兴趣的:(React学习笔记)