useState

function Counter() {

  const [count, setCount] = useState(0);
  
  const handleClick = () => {
    setCount(count + 1);
  };

  return (
    <div>
      <p>当前计数器的值为:{count}</p>
      <button onClick={handleClick}>增加计数器</button>
    </div>
  );
}

功能
用于在函数组件中定义和管理状态。

用法

const [count, setCount] = useState(0)
 设置一个count用来存储计数器的状态,使用setCount修改count所存储的状态。
 useState(0)中的0为初始状态,即在未修改状态之前,count的值为0。

注意:
1.在使用setCount修改状态后,并不能立即拿到修改的状态,而是在React重新渲染组件后,将新的状态应用到count变量上。即count 的改变会在下一次组件渲染时生效。
这是因为React 使用"批处理"的机制来优化状态更新。即在同一个事件处理函数中多次调用 setCount,React 会将这些更新合并为一个单独的更新,并进行一次重新渲染。
2.如果修改的状态结构是对象,那么需要将原对象进行复制,在修改

//添加一个c:3
  const [obj, setObj] = useState({a:1,b:2});
  setObj(...obj,{c:3})

如果是多层嵌套,修改里面的内容使用…进行浅拷贝可能会有一些复杂,此时我们可以使用Immer来简化更新逻辑

const person = {
	name:'huahua',
	age:2,
	hobby:{
		sing:'abc',
		play:'guitar'
	}	
}
  const [obj, setObj] = useState(person)
  
//这里我们将‘guitar’改为‘piano’
	setObj({
		...person, //复制person
		hobby:{ //替换hobby
		...person.hobby, //复制hobby
		play:'piano' //替换为‘piano’
		}
	})
	
//使用Immer修改
//首先需要安装依赖 npm install use-immer
  const [obj, updateObj] = useImmer(person)
  updateObj(draft => {
	draft.hobby.play='piano'
  })

useRef

function App() {
  const inputRef = useRef(null)
  const handleClick = () => {
    console.log(inputRef);  
  }
  return (
    <>
      <input  type="text" ref={inputRef}/>
      <button onClick={handleClick}></button>
    </>
  )
}

useState 多次设置 无效_useState 多次设置 无效


useState 多次设置 无效_react.js_02


功能

获取DOM元素。

用法
const inputRef = useRef(null)
定义一个变量inputRef,使用useRef并将初始值设置为null。在需要获取的DOM元素上绑定ref={inputRef}。
使用 inputRef.current即可获取DOM,使用inputRef.current.value即可获取当前输入的值。

注意:
使用useRef只有在组件渲染完成即DOM生成之后,才可以获取到DOM。

思考
e获取到输入框的值之后,是否可以使用inputRef.current.value动态渲染页面呢?

更改一下上面的代码,点击按钮时,将输入框的内容展示到页面上

function App() {
  const inputRef = useRef(null)
  const [showRefValue, setSHowRefValue] = useState(false)
  const handleClick = () => {
    console.log(inputRef);  
    setSHowRefValue(true)
  }
  return (
    <>
      <input  type="text" ref={inputRef}/>
      <button onClick={handleClick}>按钮</button>
      {
        showRefValue && <div>{inputRef.current.value}</div>
      }
    </>
  )
}

可以看到,第一次是可以正确的展示的。

useState 多次设置 无效_App_03


我们修改输入框的值,并再次点击按钮,123并没有变成aaa。

这是因为修改 countRef.current 不会触发组件的重新渲染,因此 useRef 返回的引用对象在重新渲染时保持不变。

如果希望点击按钮时,将输入框的内容展示到页面上,我们可以使用useState将输入框的值进行存储。

useState 多次设置 无效_App_04


总结

获取表单的信息时,如果需要动态展示在页面上,使用useState;如果只是收集数据做一些逻辑性的操作,使用useRef。

useEffect

功能
用于在react组件中创建由渲染本身引起的操作(ajax请求,更改DOM等)

const url = 'http://geek.itheima.net/v1_0/channels'
  useEffect(()=>{
    async function getList() {
      const res = await fetch(url)
      const list = await res.json()
      console.log(list);     
    }
    getList()
  },[])

用法
useEffect接收两个参数,第一个参数是回调函数用于执行一些操作;第二个参数是依赖项数组,用于触发回调函数的执行时机。

不同的依赖项会导致不同回调执行时间
这里我们写入的依赖项是空数组,所以该回调只在初始渲染执行一次。

function App() {
  const [count, setCount] = useState(0)
  useEffect(()=>{
    console.log('useEffect执行');
  })
  return (
    <div>
      count:{count}
      <br />
      <button onClick={()=>setCount(count+1)}>+</button>
    </div>
  )
}

这里我们没有写入依赖项,回调函数在组件初始渲染和组件更新时执行。

(初始化执行)

useState 多次设置 无效_react.js_05


(更新count状态重新渲染组件,useEffect会再次执行)

useState 多次设置 无效_javascript_06

(将依赖项变为count)

function App() {
  const [count, setCount] = useState(0)
  useEffect(()=>{
    console.log('useEffect执行');
  }, [count])
  return (
    <div>
      count:{count}
      <br />
      <button onClick={()=>setCount(count+1)}>+</button>
    </div>
  )
}

当指定依赖项时,回调函数在组件初始渲染和依赖项发生变化时执行。

(初始化执行一次)

useState 多次设置 无效_useState 多次设置 无效_07


(count发生变化 useEffect重新执行)

useState 多次设置 无效_useState 多次设置 无效_08

注意:
这里不写入依赖项和将count作为依赖项产生的结果是一样的,这是因为本例中状态只有count。
当组件中有多个状态发生改变的时候,无依赖项的回调函数依旧会重新执行;而将count作为依赖项,只有count发生变化(无论组件是否重新渲染)时会重新执行回调。

使用useEffect清除副作用
在组件卸载时我们通常需要清除一些副作用,比如:计时器(不及时清理可能会导致内存泄漏)。
副作用操作:指由渲染本身引起的对接组件外部的操作

//在卸载组件Son时清除定时器
function Son(){
  useEffect(()=>{
  setInterval(()=>{
      console.log('执行useEffect');     
    },1000)
  })
  
  return(
    <div>son component</div>
  )
}

function App() {
  const [show, setShow] = useState(true)

  return (
    <>
      {show && <Son />} 
      <button onClick={()=>setShow(false)}>卸载son组件</button>
    </>
  )
}

这里我们没有去清除副作用即未将定时器清除,所以在Son组件卸载之后,定时器依旧在执行。

useState 多次设置 无效_App_09


useState 多次设置 无效_useState 多次设置 无效_10


这里我们在Son组件卸载之后去清除定时器,可以看到定时器将不在执行。

(useEffect中的return会在组件卸载时自动执行,除了在卸载时还有一些其他的执行时机,但不常用。)

function Son(){
  useEffect(()=>{
  const timer =  setInterval(()=>{
      console.log('执行useEffect');     
    },1000)
    
  return ()=>{
    clearInterval(timer)
  }
  })

  return(
    <div>son component</div>
  )
}

function App() {
  const [show, setShow] = useState(true)

  return (
    <>
      {show && <Son />} 
      <button onClick={()=>setShow(false)}>卸载son组件</button>
    </>
  )
}

useState 多次设置 无效_App_11


useState 多次设置 无效_react.js_12

自定义hook

为了提高部分功能的复用,便于后期维护,我们通常会自定义一些hook进行逻辑复用

//展示App component的显示与隐藏
function App() {
  const [show, setShow] = useState(true)
  const handleShow = () => {
    setShow(!show)
  }
  return (
    <>
      {show && <div>App component</div>}
      <button onClick={handleShow}>show</button>
    </>
  )
}

将展示与隐藏的逻辑抽离出来,进行自定义hook。

function useShow(){
  const [show, setShow] = useState(true)
  const handleShow = () => setShow(!show)
  
  return {show, handleShow}
}

function App() {
  const {show, handleShow} = useShow()
  
  return (
    <>
      {show && <div>App component</div>}
      <button onClick={handleShow}>show</button>
    </>
  )
}

用法
1.声明一个以use开头的函数
2.在函数体内封装需要复用的逻辑
3.将组件中用到的状态使用return返回出去
4.在使用到自定义hook的组件中执行该函数,解构出需要的状态进行使用