美文网首页
React学习笔记

React学习笔记

作者: zyghhhh | 来源:发表于2020-07-20 10:39 被阅读0次

    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(
      <React.StrictMode>
        <Provider store={store}>
          <App/>
        </Provider>
      </React.StrictMode>,
      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

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

    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 (
        <div>
          //父组件传子组件 入参接受 子组件使用
          {props.count}
        </div>
      )
    }
    
    
    const App = () => {
      const [count, setCount] = useState(0)
    
      // 两个参数 第一个参数是要执行的功能函数, 第二个参数[] 只传空数组就只执行一次
      // useMemo 在渲染期间完成 而且有返回值 返回值可以参与渲染的,用法和useEffect一样 
      let double = useMemo(() => { 
        console.log('useMemo')
        return count * 2 
      }, [count === 3])
    
      return (
        <div>
          <button
            onClick={() => {
              setCount(count + 1)
            }}
          >
            click({count}) ,double:({double})
          </button>
          <Counter count={count} />
          <hr />
        </div>
      )
    }
    
    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(
        <div>
          <p>{count}</p>
          <button onClick={() => setCount(count +1)}>click me</button>
          <p>{count1}</p>
          <button onClick={useCallback(() => setCount1(count1 +1),[count])}>click me</button>
        </div>
      )
    } 
    
    export default CB
    
    • useRef
      • 简介:
        1.用来获取dom
      • 用途:
        1. 获取输入框输入的值
        2. 通过输入框的值修改别的内容 (直接在获取的dom的回调函数中修改)
        //如果获取的dom是个Input那么 实例.current.value就是输入框的值
        const inputEl = useRef(null)
      
      
      <input type="text"  ref={inputEl} />
      
      
      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 (
        <div>
          <h2>这里是context测试</h2>
          <!--2 使用实例标签包裹子组件 并传入数据-->
          <MyContext.Provider value={count}>
            <Child ></Child>
    
          </MyContext.Provider>
    
        </div>
      )
    }
    
    <!--子组件-->
    const Child = () => {
      <!-- 3 使用useContext() 获取传入的状态 --> 
      const [state, dispatch] = useContext(MyContext)
      console.log(state)
      return (
        <div>
          {state}
        </div>
      )
    
    }
    

    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 (
        <div>
          <h2>
            {props.count}
    
            {/* state */}
            <div>
              <button
                onClick={() => {
                  dispatch({
    
                    type: 'add'
                  })
                }}
              >+</button>
            </div>
            <div>
              <button
                onClick={() => {
                  //携带参数的aciton 
                  dispatch({
                    data: 5,
                    type: 'add5'
                  })
                }}
              >+5</button>
            </div>
            <div>
    
              <button
                onClick={() => {
                  dispatch({
                    type: 'reduce'
                  })
                }}
              >-</button>
            </div>
            <div>
    
              <button
                onClick={() => {
                  //携带参数的aciton 
                  dispatch({
                    data: 5,
                    type: 'reduce5'
                  })
                }}
              >-5</button>
            </div>
          </h2>
        </div>
      )
    }
    
    // 父组件
    export default () => {
    
      const [state, dispatch] = useReducer(reducer, initstate)
      return (
        <div>
          hello word
          {/* 父组件用context对象标签 包裹子组件树 并把dispatch传递给子组件 子组件中去分发action触发reducetion 改变数据 */}
          <MyContext.Provider value={dispatch}>
            <Child count={state} />
          </MyContext.Provider>
        </div>
      )
    }
    
    
    

    自定义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学习笔记

          本文链接:https://www.haomeiwen.com/subject/tvtbjhtx.html