总结版2

## 一：受控表单绑定
通过react状态去控制Input框的状态
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3899383/174c287c-a7ab-4891-856f-7bd6e6432b43.png)
监听输入框内容变化，每当用户输入时，e.target.value（输入框的值）会更新 value，从而实现输入框内容与状态同步。
```java
import {useState} from "react"
// 1.准备一个React状态值
const [value,setValue] = useState("")
// 2.通过value属性绑定状态，通过onChange属性绑定状态同步的函数
// 通过事件参数e拿到输入框的最新的值，反向修改到react状态身上
<input
  type="text"
  value={value}
  onChange={(e) => setValue(e.target.value)}
/>
```
## 二：react中获取dom(使用useRef)
使用useRef获取input输入框的DOM对象
①1.useRef生成ref对象，并与JSX绑定
②2.dom可用时，通过inputRef.current拿到dom对象 
```java
import {useRef} from "react";

function App(){
   // 1. 创建一个 ref 对象，并初始化为 null
   const inputRef = useRef(null)

   // 2. 定义一个函数，点击按钮时获取 input 的 DOM 对象
   const showDom =()=>{
      console.log(inputRef.current)  // 输出 input 的 DOM 对象
   }
   return(
     <div>
        {/* 3. 绑定 ref 到 input 元素 */}
       <input type="text" ref={inputRef} />
        {/* 4. 点击按钮时执行 showDom 函数 */}
       <button onClick={showDom}>获取dom</button>
     </div>
   )
}
```
## 三：案例-发表评论
#### 1.核心功能实现
①用数控绑定方式，可以把用户输入的东西，收集到content里面
②点击时发布评论，替换content字段，收集到的数据是什么就绑定什么
③延续数据修改不可变的方式，原本的所有数据都展开，增加一条自己的数据
※需求：获取评论内容，点击发布按钮发布评论
⭐setCommentList([...commentList, newComment])：
...commentList 复制已有的评论列表。
{...} 创建一个新评论对象，并添加到列表中。
setCommentList() 更新 commentList 状态，让页面重新渲染，显示新评论。
```java
 // 发表评论
const [content,setContent] = useState("")
const handPublish =()=>{
  setCommentList([
      ...commentList,
      {
         rpid:100,
         user:{
            uid:"30009257",
            avatar,
            uname:"黑马前端",
         },
         content:content,
         ctime:"10-19 09:00",
         like:66,
         }
  ])
}
// 评论框
<textarea
    className="reply-box-textarea"
    placeholder="发一条友善的评论"
    value={content}
    onChange={(e)=>setContent(e.target.value)}
/>
// 发布按钮
<div className="reply-box-send">
   <div className="send-text" onClick={handPublish}>发布</div>
</div>   
```
#### 2.id和时间处理
需求：rpid要求一个唯一的随机数id(uuid)
     ctime要求以当前时间为标准，生成固定格式（dayjs）
```java
// 安装uuid
npm install uuid
// 使用方式
 import {v4 as uuidv4} from "uuid";
 uuidv4();

 // 安装Day.js
 npm install dayjs
 // 使用方式
 import dayjs from "dayjs"
 Format
```  
```java
{
import {v4 as uuidV4} from "uuid"
import dayjs from "dayjs"
         rpid:uuidV4(), //随机id
         user:{
            uid:"30009257",
            avatar,
            uname:"黑马前端",
         },
         content:content,
         ctime:dayjs(new Date()).format("MM-DD hh:mm"),// 格式化 月-日 时:分
         like:66,
         }
```
#### 3.清空内容和聚焦实现
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3899383/3d12200b-4888-4089-8f80-1084deb34acf.png)
需求：清空内容-把控制input框的value状态设置为空串
      重新聚焦-拿到input的dom元素，调用focus方法
      inputRef.current.focus() 让输入框自动获取焦点
```java
// 1.清空输入框内容
setContent("")
// 2.重新聚焦 dom(useRef) -focus
const inputRef = useRef(null)
inputRef.current.focus()
// 评论框
<textarea
    className="reply-box-textarea"
    placeholder="发一条友善的评论"
    ref={inputRef}
    value={content}
    onChange={(e)=>setContent(e.target.value)}
/>    
```      
## 四：父子组件通信
组件之间的数据传递
#### 1.父传子基础实现
步骤：①父组件传递数据-在子组件标签上绑定属性
②子组件接收数据-子组件通过props参数接收数据
props：包含着父组件传递过来的所有数据
父组件App通过props向子组件Son传递数据，然后子组件Son在渲染时接收并显示这个数据。
```java
function Son(props){
  console.log(props)
  return <div> this is son,{props.name}</div>
}

function App(){
   const  name = "this is app name"
   return(
      <div>
        <Son name = {name}/>  {/* 传递name变量给Son组件 */}
      </div>
   )
} 
```
#### 2.props说明
可以传递任意的数据：数字，字符串，布尔值，数组，对象，函数，jsx
props是只读对象，子组件只能读取props中的数据，不能直接进行修改，父组件的数据只能有父组件修改
#### 3.父传子children说明
父组件通过children传递子元素给Son组件，然后Son组件可以通过 props.children渲染这个子元素。  
props.children：Son组件的子元素
```java
function Son(props){
  console.log(props)
  // props.children在这里是<span> this is span </span>
  return <div> this is son,{props.children}</div>
}

function App(){
   return(
      <div>
        <Son>
          <span> this is span</span>
        </Son>
      </div>
   )
} 
```
#### 4.子传父实现
在子组件中调用父组件中的函数并传递实参
父组件通过props传递一个函数getMsg给子组件，然后子组件在点击按钮时调用这个函数，并把自己的数据sonMsg传回给父组件。
```java
function Son({onGetSonMsg}){
   // Son组件中的数据
   const sonMsg = "this is son msg"
   return(
      <div>
        this is Son
        <button onClick ={()=>onGetSonMsg(sonMsg)}>sendMsg</button>
      </div>
   ) 
}

function App(){
  const [msg,setMsg] = useState("")
  const getMsg = (msg)=>{
    console.log(msg)
    setMsg(msg)
  }
   return(
      <div>
          this is App,{msg}
         <Son onGetSonMsg = {getMsg}/>
      </div>
   )
}    
```
## 五：useEffect
#### 1.概念理解和基础使用
用来执行一些“自动触发的操作”，而不是等到用户点击按钮、输入内容等事件才执行。可以想象成一个“自动运行的代码块”：当组件加载（渲染）时，就会自动执useEffect里的代码，当某些数据变化时（比如 state 发生变化），useEffect也可以自动执行。
useEffect 不需要用户手动触发（比如点击按钮），它会在页面加载或状态变化时自动执行。比如：页面一打开就执行代码，或某个状态变化后自动执行代码
你只管写在useEffect里，React会帮你找合适的时机自动执行！
```java
useEffect(() =>{},[])
// 参数1是一个函数，在函数内部可以放置要执行的操作
// 参数2是一个数组，在数组里放置依赖项，不同依赖项会影响第一个参数函数的执行，
//                 当是一个空数组的时候，只会在组件组件首次渲染时执行一次
    
```
⭐作用
①首次进入页面后，初始化操作
②监视某个值的变化
③退出页面后，执行收尾操作
#### 2.不同依赖项说明
①空数组依赖：只在初始渲染时执行一次
```java
import { useEffect, useState } from "react";

function App() {
  // 2.传入空数组依赖  初始执行一次
  useEffect(()=>{
    console.log("副作用函数执行了")
  },[])
  return (
    <div>
         this is App
         <button onClick ={()=>setCount(count +1)}>+{count}</button>
    </div>
  )
}
export default App; 
```
②添加特定依赖项：组件初始渲染＋特性依赖项变化时执行
```java
import { useEffect, useState } from "react";

function App() {
  const [count,setCount] = useState(0)
  // 3.传入特定依赖项  初始 ＋ 依赖项变化时执行
  useEffect(()=>{
    console.log("副作用函数执行了")
  },[count])
  return (
    <div>
         this is App
         <button onClick ={()=>setCount(count +1)}>+{count}</button>
    </div>
  )
}
export default App;    
```
#### 3.清除副作用
在useEffect里，“副作用”指的是组件渲染时需要执行的额外操作，比如：
发送网络请求，操作 DOM，订阅事件，定时器。这些操作不是组件渲染UI所必需的，但又是程序运行时需要的，所以叫“副作用”。
React提供了清理副作用的方法，就是在useEffect里返回一个函数，这个函数会在组件卸载或useEffect重新执行时运行，负责清除不需要的副作用。
useEffect是“开灯”，但如果灯一直亮着没人关，就会浪费电。所以React允许你在组件卸载时“关灯”，就是清除副作用！
```java
 useEffect(() =>{
    // 实现副作用操作逻辑
    return()=>{
    // 清除副作用逻辑
    }
 },[])  
```
在Son组件渲染时开启一个定时器，卸载时清除这个定时器
①Son 组件在渲染时启动一个定时器，每隔 1 秒输出 "定时器执行中"。
②App 组件包含一个 show 状态，控制 Son 组件是否显示。
③点击按钮 setShow(false) 会卸载 Son 组件，同时 useEffect 清理定时器，防止定时器继续运行。
```java
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实现
useToggle是一个自定义Hook，用于管理 true/false 状态。
好处是让toggle逻辑可以复用，多个组件可以直接使用。
按钮点击时，toggle() 切换 value，从而控制 <div> 显示/隐藏。
```java
import { useEffect, useState } from "react";

// 封装自定义hook

// 1.声明一个以use打头的函数
// 2.在函数体内封装可复用的逻辑 只要是可复用的逻辑
// 3.把组件中用到的状态或者回调return出去 以对象或者数组
// 4.在哪个组件中要用到这个逻辑，就执行这个函数，解构出来状态和回调进行使用
function useToggle(){
  // 可复用的逻辑代码
  const [value,setValue] = useState(true)
  const toggle = ()=>setValue(!value)

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

function App() {
  const {value, toggle} = useToggle()
  return (
    <div>
        {value && <div>this is div</div>}
         <button onClick ={toggle}>toggle</button>
    </div>
  )
}
export default App;    
```
## 七：reacthooks使用规则说明
1.只能在组件中或者其他自定义hook函数中调用
2.只能在组件的顶层调用，不能嵌套在if for其他函数中