React学习2

不公天道,吾自立身,不朽不名,八荒春风,春风已至,不见静春,天下有我齐静春,天下快哉,我亦快哉。---------《剑来》齐静春

1.受控绑定

import { useRef, useState } from "react";
/** *受控绑定通过react状态控制input框的状态
 * 1,声明一个react状态-useState
 * * **********
 * 2、核心绑定流程
 *
 * 1,通过value属性绑定react状恋
 * 2、绑定onChange事件通过事件参数e拿到输入框最新的值 反向修改到react状态
 * */
const Formbind = () => {
  // input绑定值
  const [value, setValue] = useState("");
  return (
    <>
      <h3>表单受控绑定</h3>
      <input
        type="text"
        value={value}
        onChange={(e) => setValue(e.target.value)}
      />
    </>
  );
};
export default Formbind;

2.获取DOM

import { useRef, useState } from "react";
/** React中获取D0N****
 * 1.useRef生成ref对象 绑定到dom标签身上
 * 2.dom可用时,ref.current获取dom
 * 渲染完毕后,dom生产之后才可用
 * */
const Formbind = () => {
  // input绑定值
  const [value, setValue] = useState("");
  // 定义input的ref
  const inputRef = useRef(null);
  // 获取input的dom
  const handleGetDom = () => {
    console.log(inputRef.current);
    console.dir(inputRef.current);
  };
  return (
    <>
      <h3>表单受控绑定</h3>
      <input
        type="text"
        value={value}
        onChange={(e) => setValue(e.target.value)}
        ref={inputRef}
      />
      <h3>获取表单dom</h3>
      <button onClick={handleGetDom}>获取DOM</button>
    </>
  );
};
export default Formbind;

3.组件通信

1.父传子

// 父传子
//1.父组件传递数据   子组件标签身上绑定属性
//2.子组件接收数据    props的参数
/**
 * 1.props可传递任意的数据数字、字符串、布尔值、数组、对象、函数、JSX
 * 2.props是只读对象
 * 子组件只能读取props中的数据,不能直接进行修改,父组件的数据只能由父组件修改
 *父传子-特殊的prop children
场景:当我们把内容嵌套在子组件标签中时,父组件会自动在名为children的prop属性中接收该内容
 * */

import { useState ,useContext } from "react";

//子组件
function Son(props) {
  // props:对象里面包含了父组件传递过来的所有的数据
  console.log("props", props);
  // Cannot assign to read only property 'name' of object '
  //props.name = "newName"; 错误写法 props是只读的会报错
  return (
    <div>
      子组件名字:{props.name},jsx:{props.child},children:{props.children}
    </div>
  );
}

//父组件
function ComCommunication() {
  // 父传子
  const name = "this is app";
  return (
    <>
      <h3>父子通信</h3>
      <h4>父传子参数</h4>
      <Son
        name={name}
        age={18}
        isTrue={true}
        list={["vue", "react"]}
        obj={{ name: "jack" }}
        fun={() => console.log(123)}
        child={<span>this is span</span>}
      />
      <h4>特殊父传子</h4>
      <Son>
        <span>第二个组件</span>
      </Son>
    </>
  );
}
export default ComCommunication;

2.子传父

//核心:在子组件中调用父组件中的函致并传递实参

import { useState ,useContext } from "react";

//核心:在子组件中调用父组件中的函致并传递实参
function Son2({ onGetMsg }) {
  const sonMsg = "Son2";
  return (
    <div>
      <div>我是Son2组件</div>
      <button onClick={() => onGetMsg(sonMsg)}>子传父按钮</button>
    </div>
  );
}

function ComCommunication() {
  // 定义接收子msg的状态
  const [msg, setMsg] = useState("");
  // 接受子msg的函数
  const getMsg = (msg) => {
    console.log("msg", msg);
    setMsg(msg);
  };
  return (
    <>
      <h4>子传父参数</h4>
      <Son2 onGetMsg={getMsg}></Son2>
      <div>接收到son2的name:{msg}</div>
    </>
  );
}
export default ComCommunication;

3.兄弟传参(状态提升)

/**

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

 *1. A组件先通过子传父的方式把数据传给父组件App

 *2. App拿到数据后通过父传子的方式再传递给B组件

 */

import { useState ,useContext } from "react";

// 兄弟传参
/**状态提升机制 */
// 1.通过子传父 A -> ComCommunication
//2、通过父传子ComCommunication->B

function A({ onBGetMsg }) {
  const bMsg = "我是你的哥哥A";
  return (
    <div>
      我是A组件
      <button onClick={() => onBGetMsg(bMsg)}>我要给弟弟B发消息</button>
    </div>
  );
}

function B({ msg }) {
  return <div>我是B组件,A传给我的msg:{msg}</div>;
}

function ComCommunication() {
  // 兄弟传参
  const [bmsg, setBmsg] = useState("");
  // 从A获取msg
  const getBmsg = (msg) => {
    setBmsg(msg);
  };
  return (
    <>
      <h4>兄弟传参(状态提升)</h4>
      <A onBGetMsg={getBmsg}></A>
      <B msg={bmsg}></B>
    </>
  );
}
export default ComCommunication;

 4.跨层通信(Context机制)

/**

 *

 * 实现步骤:

 *1.使用createContext方法创建-一个上下文对象Ctx

 *2.在顶层组件(App)中通过Ctx.Provider 组件提供数据

 *3.在底层组件(B)中通过useContext钩子函数获取消费数据

 *

 */

import { useState ,useContext } from "react";
import { createContext } from "react";

// //App->A->B
// 1. createContext方法创建一个上下文对象
// 2.在顶层组件通过Provider组件提供数据
// 3.在底层组件通过useContext钩子函数使用数据

const MsgContext = createContext();

function C() {
  return (
    <div>
      我是组件C
      <D></D>
    </div>
  );
}

function D() {
  const msg = useContext(MsgContext);
  return <div>我是组件D,我收到了ComCommunication的信息:{msg}</div>;
}


function ComCommunication() {
  const name = "this is app";
  return (
    <>
      <h4>跨层通信(Context机制)</h4>
      <MsgContext.Provider value={name}>
        <C></C>
      </MsgContext.Provider>
    </>
  );
}
export default ComCommunication;

 4.useEffect函数的使用

/**

 * useEffect的概念理解

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

 * useEffect(()=>{},[])

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

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

 *

 */

 1.发送请求示例

import { useEffect, useState } from "react";

function UseEffectMethods() {
  // 接口地址
  const URL = "http://geek.itheima.net/v1_0/channels";
  // 创建存储状态
  const [efList, setEfList] = useState([]);
  // 发送请求
  useEffect(() => {
    async function getList() {
      let res = await fetch(URL);
      const JsonRes = await res.json();
      setEfList(JsonRes.data.channels);
    }
    getList();
  }, []);
  return (
    <div>
      <h3>useEffect的使用</h3>
      <h4>接口调用</h4>
      {efList.map((item) => (
        <li key={item.id}>{item.name}</li>
      ))}
    </div>
  );
}
export default UseEffectMethods;

2.不同依赖项,函数执行影响

函数依赖项执行时机
useEffect(() => {
    console.log("1");
  });
没有第二个参数初始化 + 任意状态改变
useEffect(() => {
    console.log("1");
  },[]);
[]初始化
useEffect(() => {
    console.log("1");
  },[count]);
[count]初始化 + count状态改变

测试代码如下


import { useEffect, useState } from "react";

function TestUseEffect() {
  const [count, setCount] = useState(0);
  const [count2, setCount2] = useState(0);
  // 运行时机  初始化 + 状态改变
  /* useEffect(() => {
    console.log("TestUseEffect组件useEffect运行初始化 + 状态改变");
  }); */
  // 运行时机  初始化
  /*  useEffect(() => {
    console.log("TestUseEffect组件useEffect运行初始化");
  }, []); */
  // 运行时机  初始化 + 特定状态改变
  useEffect(() => {
    console.log("TestUseEffect组件useEffect运行初始化 + 特定状态改变");
  }, [count2]);
  return (
    <div>
      <button onClick={() => setCount(count + 1)}>{count}</button>
      <button onClick={() => setCount2(count2 + 1)}>{count2}</button>
    </div>
  );
}


function UseEffectMethods() {
  return (
    <div>
      <h3>useEffect的使用</h3>
      <h4>测试useEffect依赖不一样用法</h4>
      <TestUseEffect></TestUseEffect>
    </div>
  );
}
export default UseEffectMethods;

 3.清除副作用

/**

 * useEffect-清除副作用

 * useEffect(()=>{

 *  //实现副作用逻辑操作

 *  return()=>{

 *    //清除副作用操作

 *  }

 * },[])

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

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

 *

 *

 */

import { useEffect, useState } from "react";

// useEffect-清除副作用
function RemoveSideEffect() {
  useEffect(() => {
    let timer = setInterval(() => {
      console.log("我是RemoveSideEffect的定时器,运行中....");
    }, 1000);
    //如果不清除副作用则就算该组件不在页面中,则该定时器依旧在运行
    return () => {
      clearInterval(timer);
    };
  }, []);
  return <div>我是组件RemoveSideEffect</div>;
}



function UseEffectMethods() {
  const [show, setShow] = useState(true);
  return (
    <div>
      <h3>useEffect的使用</h3>
      <h4>清除副作用</h4>
      {show && <RemoveSideEffect></RemoveSideEffect>}
      <button onClick={() => setShow(false)}>卸载组件RemoveSideEffect</button>
    </div>
  );
}
export default UseEffectMethods;

5.自定义HOOK封装 

8ba0e433107d452a996d4939a2e02b08.png

808288fee1bb4facbbea9041635a80c9.png

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

// 1.声明一个以use打头的函数

// 2.在函数体内封装可复用的逻辑(只要是可复用的逻辑)

//3.把组件中用到的状态或者回调return出去(以对象或者数组)

//4.在哪个组件中要用到这个逻辑,就执行这个函数,解构出来状态和回调进行使用

// 封装自定义hook
//问题:布尔切换的逻辑 当前组件耦合在一起的 不方便复用
// 解决思路 自定义hook
import { useState } from "react";


// 1.声明use打头的函数
function useToggle() {
  // 2.可复用逻辑代码
  const [value, setValue] = useState(true);
  const toggle = () => {
    setValue(false);
  };
  //3.return出 需要在其他组件中使用 状态和回调函数
  return {
    value,
    toggle,
  };
}



function EncapsulationHook() {
  //4.执行函数,解构出来状态和回调进行使用
  const { value, toggle } = useToggle();
  return (
    <div>
      <h3>自定义hook</h3>
      {value && <div>this is div</div>}
      <button onClick={toggle}>toggle</button>
    </div>
  );
}
export default EncapsulationHook;

 6.useMemo

用于消耗非常大的计算,具有缓存的作用,跟vue的computed相似(主要用来缓存值)

import { useMemo, useState } from 'react'
// 缓存:消耗非常大的计算的时候
// 定义斐波那契数列求和
const fibonacci = (n) => {
  console.log('计算函数执行了', n)
  if (n < 3) return 1
  return fibonacci(n - 1) + fibonacci(n - 2)
}
const UseMemo = () => {
  console.log('组件重新渲染了')
  const [count1, setCount1] = useState(0)


  //useMemo包裹后,count2改变的时候斐波那契数列求和函数就不会重新执行,有缓存的作用,只有count1改变的时候,才会执行计算函数
  const result = useMemo(() => fibonacci(count1), [count1])

  const [count2, setCount2] = useState(0)
  return (
    <div>
      <h3>useMemo的使用(计算属性)</h3>
      <button onClick={() => setCount1(count1 + 1)}>
        changeCount1:{count1}
      </button>
      <button onClick={() => setCount2(count2 + 1)}>
        changeCount2:{count2}
      </button>
      {result}
    </div>
  )
}
export default UseMemo

7.React.Memo

使用 memo 将组件包装起来,以获得该组件的一个 记忆化 版本

React 通常在其父组件重新渲染时重新渲染一个组件。你可以使用 memo 创建一个组件,当它的父组件重新渲染时,只要它的新 props 与旧 props 相同时,React 就不会重新渲染它。这样的组件被称为 记忆化的(memoized)组件。

1.默认的渲染机制是子跟着父一起渲染

2.memo进行缓存只有props发生变化的时候才会重新渲染(不包含context的情况,state改变的情况)

需要注意的是:

React.memo props比较机制

1.传递一个简单类型的prop-----  prop变化时组件重新渲染

2.传递一个引用类型的prop-----  比较的是新值和旧值的引用是否相等 当父组件的函数重新执行时,实际上形成的是新的数组引用

3.解决问题2的情况使用useMemo函数包裹引用类型数据: 保证引用稳定 -> useMemo  组件渲染过程中缓存一个值

import { memo, useMemo, useState } from 'react'
const Son = memo(({ count, title }) => {
  console.log('子组件渲染了', title)
  return (
    <div>
      son{title}---{count}
    </div>
  )
})

const ReactMemo = () => {
  const [count, setCount] = useState(0)

  //1.传递一个简单类型的prop  prop变化时组件重新渲染
  const num = 100


  // 2.传递一个引用类型的prop  比较的是新值和旧值的引用是否相等 当父组件的函数重新执行时,实际上形成的是新的数组引用
  const arr = [1, 2, 3]

  // 3.保证引用稳定 -> useMemo  组件渲染过程中缓存一个值
  const arr2 = useMemo(() => [1, 2, 3], [])
  return (
    <div>
      <h3>react.Memo的使用(缓存子组件,只要props发生变化才执行) </h3>
      <button onClick={() => setCount(count + 1)}>changeCount:{count}</button>
      <Son count={count} title={'A--'} />
      <Son count={num} title={'B--'} />
      <Son count={arr} title={'C--'} />
      <Son count={arr2} title={'D--'} />
    </div>
  )
}
export default ReactMemo

运行结果如下:(图1 是初始化;图2 count1改变时运行结果)

2f6c0cc4968046858a29faaa7e031207.pngcf2413ad4be94f839d9eca2ebf49176b.png

 

1.使用 state 更新记忆化(memoized)组件 

即使一个组件被记忆化了,当它自身的状态发生变化时,它仍然会重新渲染。memoization 只与从父组件传递给组件的 props 有关。

 如果将 state 变量设置为其当前值,即使没有使用 memo,React 也会跳过重新渲染组件。你仍然可能会看到额外地调用组件函数,但其结果将被丢弃。

const Son = memo(({ count, title }) => {
  const [num, setNum] = useState(0)
  //num2改变子组件是一定会渲染的
  const [num2, setNum2] = useState(0)
  console.log('子组件渲染了', title)
  return (
    <div>
      son{title}---{count}
      <br />
      {/*设置为当前状态子组件不会渲染,这边再改变num2之后调num按钮是因为num2改变的时候num按钮重新渲染了,所以会执行一次*/}
      <button onClick={() => setNum(num)}>num:{num}</button>
      <button onClick={() => setNum2(num2 + 1)}>num2:{num2}</button>
    </div>
  )
})

2.使用 context 更新记忆化(memoized)组件 

即使组件已被记忆化,当其使用的 context 发生变化时,它仍将重新渲染。记忆化只与从父组件传递给组件的 props 有关。

8.useReducer

useReducer 是一个 React Hook,它允许你向组件里面添加一个 reducer

//const [state, dispatch] = useReducer(reducer, initialArg, init?)
//reducer:用于更新 state 的纯函数。参数为 state 和 action,返回值是更新后的 state。state 与 action 可以是任意合法值。
initialArg:用于初始化 state 的任意值。初始值的计算逻辑取决于接下来的 init 参数。
可选参数 init:用于计算初始值的函数。如果存在,使用 init(initialArg) 的执行结果作为初始值,否则使用 initialArg。


import { useReducer } from 'react'
// 定义reducer函数
const reducer = (state, action) => {
  switch (action.type) {
    case 'DEC':
      return state - 1
    case 'INC':
      return state + 1
    case 'SET':
      return action.payload
    default:
      return state
  }
}
const UseReducer = () => {
  // 使用useReducer
  const [state, dispatch] = useReducer(reducer, 0)
  return (
    <div>
      <h3>useReducer的使用</h3>
      <button onClick={() => dispatch({ type: 'DEC' })}>-</button>
      <span>{state}</span>
      <button onClick={() => dispatch({ type: 'INC' })}>+</button>
      <button onClick={() => dispatch({ type: 'SET', payload: 100 })}>
        设置
      </button>
    </div>
  )
}
export default UseReducer

1.immer插件

//1.下载
npm install immer use-immer
//2.引入
import { useImmer , useImmerReducer } from "use-immer";
//3.使用
const [age, setAge] = useImmer(20); //相当于useState
const [state, dispatch] = useImmerReducer(reducer, initialState);  //相当于useRdeucer

9.useCallback

useCallback 是一个允许你在多次渲染中缓存函数的 React Hook。

0b5c8e6ff68f4b6281c4abbee4b746d2.png

import { memo, useState, useCallback } from 'react'

const Son = memo(({ onChange }) => {
  console.log('子组件渲染了')
  return (
    <>
      <input type='text' onChange={(e) => onChange(e.target.value)} />
    </>
  )
})

const UseCallback = () => {
  const [count, setCount] = useState(0)
  //不使用  useCallback 函数的话,在count发生改变的时候,会重新渲染子组件
  const handleChange = useCallback((val) => {
    console.log(val)
  }, [])
  return (
    <div>
      <h3>useCallback(缓存函数)</h3>
      <Son onChange={handleChange}></Son>
      <button onClick={() => setCount(count + 1)}>count{count}</button>
    </div>
  )
}
export default UseCallback

 10.forwardRef&useImperativeHandle

1.forwardRef

允许组件使用 ref 将 DOM 节点暴露给父组件。


import { forwardRef, useImperativeHandle, useRef } from 'react'
const Son = forwardRef((props, ref) => {
  return (
    <>
      <input type='text' ref={ref} />
    </>
  )
})
const ForwardRefanduseImperativeHandle = () => {
  const ref = useRef(null)
  // 获取子组件的dom,执行focus事件
  const getDom = () => {
    ref.current.focus()
  }

  return (
    <div>
      <h3>forwardRef与useImperativeHandle的使用</h3>
      <Son ref={ref}></Son>
      <button onClick={getDom}>聚焦</button>
    </div>
  )
}
export default ForwardRefanduseImperativeHandle

2.useImperativeHandle

useImperativeHandle是 React 中的一个 Hook,它能让你自定义由 ref 暴露出来的句柄。

/**
 * 如果使用useImperativeHandle函数暴露方法的时候那么子组件的ref需要自己在自定义绑定,否则获取不到ref,如果只获取dom,则只要forwardRef
 *
 */

import { forwardRef, useImperativeHandle, useRef } from 'react'
const Son = forwardRef((props, ref) => {
  // 定义dom的ref
  const ref2 = useRef(null)
  const ref3 = useRef(null)
  const getFocus = () => {
    console.log('222')
    ref2.current.focus()
  }
  const getFocus2 = () => {
    console.log('111111')
    ref3.current.focus()
  }
  // 把方法暴露出去
   useImperativeHandle(ref, () => {
    return {
      getFocus2,
      getFocus
    }
  }) 
  return (
    <>
      <input type='text' ref={ref3} />
      <input type='text' ref={ref2} />
    </>
  )
})
const ForwardRefanduseImperativeHandle = () => {
  const ref = useRef(null)
  // 获取子组件的方法
  const getMethod = () => {
    ref.current.getFocus()
  }
  const getMethod2 = () => {
    ref.current.getFocus2()
  }
  return (
    <div>
      <h3>forwardRef与useImperativeHandle的使用</h3>
      <Son ref={ref}></Son>
      <button onClick={getMethod}>子组件方法聚焦</button>
      <button onClick={getMethod2}>子组件方法聚焦2</button>
    </div>
  )
}
export default ForwardRefanduseImperativeHandle

11.类组件用法

import { Component, useState } from 'react'
class ClassCounter extends Component {
  //编写组件的逻辑代码
  //1、状态变量2.事件回调3.UI(JSX)
  //1,定义状态变量
  state = {
    count: 0
  }
  setCount = () => {
    //修改状态变量
    this.setState({
      count: this.state.count + 1
    })
  }
  // 声明周期函数
  //组件渲染完毕执行一次发送网络请求
  componentDidMount() {
    //组件挂载完成
    console.log('组件挂载完成')
    this.timer = setInterval(() => {
      console.log('定时器...')
      this.setState({
        count: this.state.count + 1
      })
    }, 1000)
  }
  // 组件卸载的时候自动执行 副作用清理的工作 清除定时器 清除事件绑定
  componentWillUnmount() {
    //组件卸载
    console.log('组件卸载')
    clearInterval(this.timer)
  }
  render() {
    return (
      <>
        <button onClick={this.setCount}>加{this.state.count}</button>
      </>
    )
  }
}

class Son extends Component {
  state = {
    num: 100
  }
  render() {
    return (
      <div>
        <h4>父传子</h4>
        我是子组件{this.props.msg}
        <h4>子传父</h4>
        <button onClick={() => this.props.onGetNum(this.state.num)}>
          子传父
        </button>
      </div>
    )
  }
}
function Counter() {
  const [show, setShow] = useState(true)
  const getNum = (val) => {
    console.log('子组件传回来的', val)
  }
  return (
    <div>
      <h3>class类组件</h3>
      <button onClick={() => setShow(false)}>卸载组件</button>
      {show && <ClassCounter></ClassCounter>}
      <Son msg='子组件 我是父组件呀.......' onGetNum={getNum} />
    </div>
  )
}
export default Counter

/**
 * 1.父传子直接通过prop子组件标签身上绑定父组件中的数据即可// 2.子传父在子组件标签身上绑定父组件中的的数,子组件中调用这个的数传递参数
  总结
  *1.思想保持一致
 * 2.类组件依赖于this
 * 
 */

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值