最新公告
  • 欢迎您光临起源地模板网,本站秉承服务宗旨 履行“站长”责任,销售只是起点 服务永无止境!立即加入钻石VIP
  • React , React-Hook, Redux

    正文概述 掘金(田田川)   2021-02-23   448

    32. React

    32.1 引入React

    第一种方法:

    script标签引入

    一般不用这个方法

    cjs, umd是什么: 模块定义 React , React-Hook, Redux 第二种方法: webpack 方法, 也很麻烦

    第三种: create-react-app

    目标一: 用cdn方法引入react , 渲染一个可以增加的n

    渲染失败:

    const React = window.React;
    const ReactDOM = window.ReactDOM;
    
    let n = 0;
    const App = React.createElement("div", null, [
      n,
      React.createElement(
        "button",
        {
          onClick: () => {
            n += 1;
            console.log(n); //这一句是精髓
            ReactDOM.render(App, document.querySelector("#app")); // 为什么还是不能重新渲染
          }
        },
        "+1"
      )
    ]);
    
    ReactDOM.render(App, document.querySelector("#app"));
    
    

    渲染成功:

    const React = window.React;
    const ReactDOM = window.ReactDOM;
    
    let n = 0;
    const App =()=> React.createElement("div", null, [
      n,
      React.createElement(
        "button",
        {
          onClick: () => {
            n += 1;
            console.log(n); //这一句是精髓
            ReactDOM.render(App(), document.querySelector("#app")); 
          }
        },
        "+1"
      )
    ]);
    
    ReactDOM.render(App(), document.querySelector("#app"));
    
    

    变量不会重新执行, 函数每次调用就会重新执行

    为啥let 不打印6个6;

    React , React-Hook, Redux

    立即求值, 延迟求值:

    React , React-Hook, Redux


    虚拟DOM

    找到虚拟DOM不同的算法: DOM Diff算法

    Facebook babel 作者招进来, 然后让他将jsx集成如babel.js

    方法一的缺陷: 在浏览器端, 下载, 翻译jxs, 非常慢

    React , React-Hook, Redux

    全都要学react 和vue

    32.2 组件和元素

    32.2.1 元素和组件的区别

    元素: 也就是虚拟DOM.

    const App = React.createElement("div", null, null)
    

    组件: 返回元素的一个函数, vue里面一个构造选项可以表示一个组件.

    const App = ()=>React.createElement("div", null, null)
    

    32.2.2 函数组件和类组件的区别

    函数组件的写法:

    function Welcome(props) {
        return <h1>Hello,{props.name}</h1>
    }
    
     //调用方法:
    <Welcome name="frank"/>
    Welcome({"name":"frank"})
    

    类组件的写法:

    class Welcom extends React.Component{
        render(){
        return <h1>Hello,{this.props.name}</h1>
        }
    }
    
    //调用方法:
    <Welcome name="frank"/>
    

    32.2.3 Babel理解jsx的逻辑

    我们的代码:

    function Welcome(props) {
        return <h1>Hello,{props.name}</h1>
    }
    

    Babel理解的:

    function Welcome(props) {
      return /*#__PURE__*/React.createElement("h1", null, "Hello,", props.name);
    }
    

    可以通过babel online查看babel的理解:https://babeljs.io/repl

    React.createElement 的逻辑

    • 如果传入一个字符串div, 就会创建一个虚拟dom.
    • 如果传入一个函数, 就会调用函数,获取其返回值
    • 如果传入一个, 就会调用一个new, 新建一个对象, 调用对象的render方法, 获取其返回值

    32.2.4 用两种方法创建组件

    function App() {
        let messageForSon ="儿子好"
        return (
            <div className="App">
                爸爸
                <Son messageForSon={messageForSon} />
            </div>
        );
    }
    class Son extends React.Component{
        constructor() {
            super();
            this.state ={
                n:0
            }
        }
        add(){
            this.setState({n:this.state.n +1})
        }
        render(){
            return(
                <div className="Son">
                    消息:{this.props.messageForSon}
                    儿子 n:{this.state.n}
                    <button onClick={()=>this.add()}>+1</button>
                    <GrandSon messageForSon='孙子好'/>
                </div>
            );
        }
    }
    const GrandSon =(props)=>{
        const [n,setN]=React.useState(0)
        return(
            <div className="Grandson">
                消息:{props.messageForSon}
                孙子 n:{n}
                <button onClick={()=>setN(n+1)}>+1</button>
            </div>
        )
    }
    

    踩的坑:

    保存:

    react-dom.development.js:13231 Uncaught Error: Objects are not valid as a React child (found: object with keys {n}). If you meant to render a collection of children,

    原因: jsx语法会自动调用React.CreateElements, 所{this.state}是一个对象, 作为参数就会报错:Objects are not valid as a React child .

    //错误的:
    return(
        <div id="son">
            儿子 n:{this.state}
            <button onClick={()=>this.add()}>+1</button>
        </div>
    );
    
    //正确的:
    return(
        <div id="son">
            儿子 n:{this.state.n}
            <button onClick={()=>this.add()}>+1</button>
        </div>
    );
    

    踩的坑:

    报错: react一直加载不出来, 感觉像是服务器死机了

    原因:在定义GrandSon的时候,里面引用了GrandSon,造成了死循环,死机

    const GrandSon =()=>{
        const [n,setN]=React.useState(0)
        return(
            <div className="Grandson">
                孙子 n:{n}
                <button onClick={()=>setN(n+1)}>+1</button>
                这里是错的:
                <GrandSon/>
            </div>
        )
    }
    

    32.2.5 使用props

    //方法一: 传递
    <Son messageForSon={messageForSon} />
    //方法一: 接收
    <div className="Son">
        消息:{this.props.messageForSon}
        儿子 n:{this.state.n}
        <button onClick={()=>this.add()}>+1</button>
        <GrandSon messageForSon='孙子好'/>
    </div>
    
    //方法二:传递
    <GrandSon messageForSon='孙子好'/>
    //方法二:接收
    const GrandSon =(props)=>{
        const [n,setN]=React.useState(0)
        return(
            <div className="Grandson">
                消息:{props.messageForSon}
                孙子 n:{n}
                <button onClick={()=>setN(n+1)}>+1</button>
            </div>
        )
    }
    

    32.2.6 使用state

    //方法一:
    constructor() {
            super();
            this.state ={
                n:0
            }
        }
        add(){
            this.setState({n:this.state.n +1})
        }
    
    //方法二:
    const GrandSon =(props)=>{
        const [n,setN]=React.useState(0)
        return(
            <div className="Grandson">
                消息:{props.messageForSon}
                孙子 n:{n}
                <button onClick={()=>setN(n+1)}>+1</button>
            </div>
        )
    }
    

    为什么不能直接修改n, 因为react没有监听n, 必须使用setState告诉浏览器进行重新渲染

    因为setState是异步方法, 所有这样写更好

    //原来的
    this.setState({n:this.state.n +1})  //不会马上修改state, 等会再修改
    //新的
    this.setState(state=>{
        return {n:state.n+1}
    }  
    )
    
    

    函数组件setState不会改变n, 而是产生一个新n

    React , React-Hook, Redux

    this.State({n:1})不会修改其他属性. 但是this.State({n:1, user:{name:1,age:16}}) 但是user不会合并以前数学

    这种写法不推荐: 因为修改n,会修改m

    const [state, setState]  = React.useState({
    	n:0, m:0
    });
    

    32.2.7 事件绑定

    React , React-Hook, Redux

    React , React-Hook, Redux

    用函数组件, 就不会被this折磨

    React , React-Hook, Redux

    React , React-Hook, ReduxReact , React-Hook, Redux

    React , React-Hook, Redux

    this的面试题需要画画.

    32.3 组件

    32.3.1 创建class组件的两种方式

    创建class组件

    过时的:

    import React from 'react'
    const A = React . createclass ({
       render(){
           return(
               <div>hi</div>
           )
       }
    })
    

    class组件:

    class Son extends React.Component{
        constructor(props) {
            super(props);
            this.state ={
                n:0
            }
        }
        add(){
            this.setState({n:this.state.n +1})
        }
        render(){
            return(
                <div className="Son">
                    消息:{this.props.messageForSon}
                    儿子 n:{this.state.n}
                    <button onClick={()=>this.add()}>+1</button>
                    <GrandSon messageForSon='孙子好'/>
                </div>
            );
        }
    }
    

    注意的几点:

    • extends、constructor、super强行记忆,别问为什么
    • 不能改props, 外部数据应该由外部更新
    • componentWillReceiveProps: props更新的回调函数, 已经过时了, 被弃用了
    • props: 接收外部数据, 函数
    • 钩子: 也就是特殊函数

    32.3.2 setState:延迟更新陷阱

    例如:

    onClick = ()=>{
        this.setState({
            x:this.state.x+1,//2
        })
        this.state.x//1
        this.setState({
            x:this.state.x+1,//2
        })
    }
    

    因为onClick中的语句执行完, 才会更新state. 导致**闹钟同时响效应*

    解决办法: 函数形式写更新state

        onClick2 = ( ) => {
            this.setState((state)=>({x:state.X+1}))
            this.setState((state)=>({x:state.X+1}))
    }
    

    注:this.setState(???,fn),成功更新后,会调用fn函数

    ####32.3.3 生命周期函数

    函数列表:

    • constructor()
    • shouldComponentUpdate()
    • render()
    • componentDidMount()
    • componentDidUpdate()
    • componentWillUnmount()

    constructor

    用途:

    • 初始化 props

    • 初始化state,但此时不能调用setState

    • 用来写 bind this

         constructor(){
          /*其他代码略*/
          this.onClick = this.onClick.bind(this)
      }
      
    • 可不写


    shouldComponentUpdate

    目标一: 阻止render

    • ·用途: 返回true表示不阻止UI更新 返回false表示阻止UI更新
    • 面试常问 v问:shouldComponentUpdate有什么用? 答:它允许我们手动判断是否要进行组件更新,我们可以根据应用场景灵活地设置返回值,以避免不必要的更新

    为什么说shouldComponentShould 会阻止render 而不是阻止渲染更新页面

    如下图:

    React , React-Hook, Redux

    {n:1}, {n:1}地址不一样, 所有不是同一个对象, React会render, 但是render后生成的虚拟DOM一样,不会更新页面, shouldComponentUpdate,可以用来阻止render

    具体代码:

    shouldComponentUpdate(newProps,newState){
        if(newState.n=this.state.n){
        	return false
        }else{
            return true
        }
    }
    

    我们除了自己比较, 还有更简单的做法: 使用React.pureComponent

    目标二: purecomponent 新旧state 对比


    render:

    易错:

    • render只能单个元素, 可以用React.Fragment标签包裹, 这个标签不会出现在html中. 简写<></>

    • 返回多个列表元素一定要加key

      render(){
          return this.state.array.map(n→<span key={n}>{n}</span>)
      }
      

    componentDidMount

    目标三: 获取挂载组件的宽度: getBoundingClient , 依赖Dom元素

    先声明变量, 防止程序找不到

    首次执行渲染会 执行componentDidMount


    componentDidUpdate

    • **面试官会问:**可以在哪个生命周期发起ajax请求?追问还可以在哪?

      一般在componentDidMount, 也可以在componentDidUpdate, 但是这里一般用于更新数据

    • 在此处setState可能会引起无限循环,除非放在if里

    • 若shouldCom...Update返回false,则不触发此钩子


    componentWilUnmount

    用途: 组件将要被移出页面然后被销毁时执行代码 unmount 过的组件不会再次 mount 举例

    • 如果你在c..DidMount里面监听了window scroll 那么你就要在c..WillUnmount里面取消监听
    • 如果你在c..DidMount里面创建了Timer 那么你就要在c..WillUnmount里面取消Timer
    • 如果你在c..DidMount里面创建了AJAX请求 那么你就要在c..WillUnmount里面取消请求
    • 否则你就是菜逼 原则:谁污染谁治理

    钩子执行顺序:

    React , React-Hook, Redux

    目标四: 自己画一下钩子执行顺序

    前面加了 UNSAFE_ 前缀的生命周期,都是被弃用的

    32.4 函数组件

    创建方式

    const Hello = props =><div>{props.message}</div>
    

    函数组件模拟生命周期:

    useEffect的作用

    • 解决副作用: 当渲染一次, 就会重新执行组件里面所有代码
    • 生命周期的作用

    目标一: 测试渲染调用所有代码

    import React from 'react'
    const Test =()=>{
        const [n, setN] = React.useState(0)
        const addN =()=> setN(n+1)
        console.log("执行了一次")
        return (
            <>
                <div>n: {n}</div>
                <div>
                    <button onClick={addN}>+1</button>
                </div>
            </>
        )
    }
    export default Test
    

    副作用: 每次更新state都会执行函数组件里面的代码, 就算虚拟Dom一样, 也会重新执行, useEffect用于指定哪些函数变量,才会执行哪些代码


    useEffect模拟声明周期

    useEffect

    • 模拟componentDidMount useEffect(()=>{ console.log('第一次渲染')},[])

    • 模拟 componentDidUpdate useEffect(()=>{ console.log('任意属性变更')}) useEffect(()=>{ console.log('n变了')},n)

    • 模拟 componentWillUnmount

      useEffect(()=>{
          console.log('第一次渲染')
          return()=>{
          	console.log('组件要死了')
          }
      }
      

    目标二: 模拟 willUnmount

    import React,{useEffect}from 'react'
    const Test =()=>{
        const [childVisble, setchildVisble] = React.useState(true)
        const changechildVisble =()=> setchildVisble(!childVisble)
    
        return (
            <>
                {childVisble?<Child/>:null}
                <div>
                    <button onClick={changechildVisble}>show</button>
                    <button onClick={changechildVisble}>hide</button>
                </div>
            </>
        )
    }
    const Child =()=>{
        useEffect(()=>{
            console.log("组件变化了")
            return ()=>{
                console.log("组件挂了")
            }
        })
        return(
            <div>child</div>
        )
    }
    export default Test
    

    目标三: 自定义hook, 实现只在第二次渲染,执行

    import React,{useEffect,useState}from 'react'
    
    const Test =()=>{
        const [n, setN]= useState(0)
        const addN =()=> setN(n+1)
        const useUpdate =(fn, m)=>{
            const [count, setcount]= useState(0)
            useEffect(()=>setcount(count+1),[m])
            useEffect(()=>{
                if(count>1){
                    fn()
                }
            },[count])
    
        }
        useUpdate(()=>console.log("我不在第一次执行"),n)
        return (
            <>
                <div>n: {n}</div>
                <div>
                    <button onClick={addN}>+1</button>
                </div>
            </>
        )
    }
    
    export default Test
    

    32.5 Hooks 原理解析

    问自己几个问题

    1. 执行setN的时候会发生什么?n会变吗App()会重新执行吗

      • setN一定会修改数据X,将n+1存入X
      • setN 一定会触发<App/>重新渲染(re-render)
    2. 如果App()会重新执行,那么 useState(0)的时候,n每次的值会有不同吗?

      useState 肯定会从×读取n的最新值

    32.5.1 简易版useState

    代码如下:

    let _state
    const myUseState =(initValue)=>{
          //!!!!  这里不用||保底值, 考虑到_State为0时,会被迫初始化
        _state = _state === undefined ? initValue:_state
        const setState=(newState)=>{
            _state = newState
    
            //这里可以让函数重新渲染
            render()
        }
        
        //!!!! 这里返回值的写法
        return [_state, setState]
    }
    
    //!!!! 这里注意渲染函数的简单实现
    const render  =()=>ReactDOM.render(
        <Test/>,
        document.getElementById('root')
    );
    
    const Test =()=>{
        const [n, setN]= myUseState(0)//useState(0)
        const addN =()=> setN(n+1)
    
        return (
            <>
                <div>n: {n}</div>
                <div>
                    <button onClick={addN}>+1</button>
                </div>
            </>
        )
    }
    
    export default Test
    

    32.5.2 解决两个state不兼容的问题

    先看这一段代码:

    let index = 0
    const f1=()=>{
      let current1 = index
      const f2 = ()=>{
        console.log(current1)
      }
      index +=1
      return f2
    }
    
    let cur1 = f1()
    let cur2 = f1()
    cur1()  // 0
    cur2()  // 1
    cur1()  // 0
    cur2()  // 1
    

    f1执行完, 局部变量current1 生命周期结束. current在f2中被固定为常量, 只要调用curl1 , 里面的current1永远是0, 和外面的current1已经没有关系了

    具体看兼容代码:

    let _state = []
    let index = 0
    const myUseState =(initValue)=>{
        const currentIndex = index
        _state[currentIndex] = _state[currentIndex] === undefined ? initValue:_state[currentIndex]
        const setState=(newState)=>{
            //!!!! curreantIndex 被setState 固定下来
            _state[currentIndex] = newState
            render()
        }
        index +=1
        return [_state[currentIndex], setState]
    }
    
    const render  =()=> {
        index = 0
        ReactDOM.render(
            <Test/>,
            document.getElementById('root')
        )
    }
    

    useState不能放在if里面

    例如代码:

    function App() {
        const [n, setN] = React.useState(0);
        let m, setM;
        
         // useState 不能放在if函数里面.
        if (n % 2 === 1) {
            [m, setM] = React.useState(0)
        }
        return (
            <div className="App">
                <p>{n}</p>
                <p>
                    <button onClick={() => setN(n + 1)}>+1</button>
                </p>
                <p>{m}</p>
                <p>
                    <button onClick={() => setM(m + 1)}>+1</button>
                </p>
            </div>
        );
    }
    

    否则会如下报错:

    原因:

    因为state存在数组里面, 不同的useState, 对应的index不同, 所以必须要确认一致的useState顺序


    解决全局_State, index的问题: React , React-Hook, Redux

    总结:

    • 每个函数组件对应一个React节点*
    • 每个节点保存着state和index
    • useState 会读取 state[index]
    • index 由 useState 出现的顺序决定
    • setState 会修改 state,并触发更新

    32.5.3 setN分身问题

    情景一:

    1. n=0, 然后:const log = ( )=>setTimeout(()=>console.log("n:${n}",3000);,设置三秒后打印n
    2. setN(n+1)
    3. 最后打印n:0

    原因: setN不改变N, n=0, n=1 都存在于内存中, 也就是setN分身问题


    如何解决setN分身问题?

    1. 使用Ref: 能够在一个组件内重新渲染的时候,Ref的值不变

      function App() {
          const nRef = React.useRef(0) // nRef: {current: 0}
      	const log = ( )=>setTimeout(()=>console.log(`n:${nRef.current}`,3000);
          return (
              <div className="App">
                  <p>{n}</p>
                  <p>
                      <button onClick={() => (nRef.current += 1)}>+1</button>
                  </p>
              </div>
          );
      }
      

      但是Ref的值变化, 不会引起组件重新渲染

      nRef.current: 变化不会重新渲染, 可以调用nRef, 重新调用一下setState:

      +  const [n, setN]= useState(0)
         
      - <button onClick={() => (nRef.current += 1)}>+1</button>
      + <button onClick={() => {nRef.current += 1,setN(nRef.current)}}>+1</button>
      
    2. useRef贯穿一个组件始终, useContext贯穿所有组件始终:

      const themeContext = React.createContext(null);
         
      function App() {
        const [theme, setTheme] = React.useState("red");
        return (
          <themeContext.Provider value={{ theme, setTheme }}>
            <div className={`App ${theme}`}>
              <p>{theme}</p>
              <div>
                <ChildA />
              </div>
              <div>
                <ChildB />
              </div>
            </div>
          </themeContext.Provider>
        );
      }
      

    总结

    • 每次重新渲染,组件函数就会执行
    • 对应的所有state都会出现「分身」
    • 如果你不希望出现分身
    • 可以用 useRef/useContext等

    32.7 redux

    一个程序员不喜欢用原生js, 那你就推荐他使用vanilla js 这个库, 体积又小, 功能又强大.

    count接收两个参数: state, action, 前者是之前的状态, 后者是要做的动作.

    React , React-Hook, Redux

    store: 存储state的地方, 获取state的方法: store.getState(), 修改state的方法: store.dispatch({type:"add"})

    不能在用钱规则counter里面使用setTimeout

    state就像钱, store就像是我的管家, 我想查看我的金额, 存钱,取钱都需要通过管家store, counter就是之前定好的规则, 我想存钱取钱,通知管家, 管家就会按照之前定好的规则操作.

    React-redux

    能让你随时访问store , 不会混乱.

    1. 先用ProVided 任命store为App总管家

      React , React-Hook, Redux

    2. 然后App组件, 以及子组件中, 通过connect告诉Store, 我需要哪些信息, 我血药哪些操作

      React , React-Hook, Redux

    3. 然后再this.props中获取需要的东西

      React , React-Hook, Redux

    32.8 React Hook

    ####32.8.1 setState

    React , React-Hook, Redux

    注意:

    1. setState, userReducer都不会合并属性.
    2. 由于setN分身问题, 所以在setN(n=>n+1)里面尽量用匿名函数

    32.8.2 useReducer

    React , React-Hook, Redux

    不合并属性, 需要用展开语法:

    React , React-Hook, Redux

    如何代替redux?

    1. 建立管家:

      React , React-Hook, Redux

    2. 定好规则:

      React , React-Hook, Redux

    3. 创建Context

      const Context = createContext(null)

    4. 创建读写api:

      React , React-Hook, Redux

    5. 将读写api 放到Context里面:

      React , React-Hook, Redux

    6. 各个组件可以使用读写api:

      React , React-Hook, Redux

    数据更新的时候, Context是逐级通知的

    32.8.3 useEffect

    effective: 副作用: 对环境的改变就是副作用.

    多个effect , 按照出现的顺序执行


    useLayoutEffect

    React , React-Hook, Redux

    改变浏览器外观后执行useEffect, useLayoutEffect在实际Dom生成进行截胡, 执行完之后,才改变浏览器外观

    大部分的时候不会在useEffect里面改变外观.但是会改变用户看到浏览器改变的时间

    为了用户体验, 优先useEffect

    32.8.4 useMemo

    修改n, 但是Child只依赖m, 也会再执行一遍:

    React , React-Hook, Redux

    具体写法: 只要props 不变, 就不会重新执行

    React , React-Hook, Redux

    有一个bug , 传值没问题, 传函数地址就有问题:

    React , React-Hook, Redux

    解决方法: 函数也用useMemo包裹:

    React , React-Hook, Redux

    React , React-Hook, Redux

    useCallback 是 useMemo的语法糖

    32.8.5 useRef

    组件重新渲染也不会变的变量:

    React , React-Hook, Redux

    React , React-Hook, Redux

    改变count.current, 不会自动render

    React , React-Hook, Redux

    forwardRef

    能够将ref参数传递到函数组件.

    React , React-Hook, Redux

    32.8.6 自定义Hook

    把hook写在一起, 然后把读和写接口暴露出去: React , React-Hook, Redux

    尽量封装在一起, 不要在组件上面写一堆hook函数

    32.8.7 stale closure

    如果一个函数包含了一个闭包, 在执行多次这个函数, 将产生多个个不同的的闭包, 如果仅记住第一个闭包, 这个闭包有可能过时.

    React , React-Hook, Redux

    React , React-Hook, Redux

    React , React-Hook, Redux


    起源地下载网 » React , React-Hook, Redux

    常见问题FAQ

    免费下载或者VIP会员专享资源能否直接商用?
    本站所有资源版权均属于原作者所有,这里所提供资源均只能用于参考学习用,请勿直接商用。若由于商用引起版权纠纷,一切责任均由使用者承担。更多说明请参考 VIP介绍。
    提示下载完但解压或打开不了?
    最常见的情况是下载不完整: 可对比下载完压缩包的与网盘上的容量,若小于网盘提示的容量则是这个原因。这是浏览器下载的bug,建议用百度网盘软件或迅雷下载。若排除这种情况,可在对应资源底部留言,或 联络我们.。
    找不到素材资源介绍文章里的示例图片?
    对于PPT,KEY,Mockups,APP,网页模版等类型的素材,文章内用于介绍的图片通常并不包含在对应可供下载素材包内。这些相关商业图片需另外购买,且本站不负责(也没有办法)找到出处。 同样地一些字体文件也是这种情况,但部分素材会在素材包内有一份字体下载链接清单。
    模板不会安装或需要功能定制以及二次开发?
    请QQ联系我们

    发表评论

    还没有评论,快来抢沙发吧!

    如需帝国cms功能定制以及二次开发请联系我们

    联系作者

    请选择支付方式

    ×
    迅虎支付宝
    迅虎微信
    支付宝当面付
    余额支付
    ×
    微信扫码支付 0 元