【React Hooks】useState 用法

回顾 React

单向数据流

和angular双向绑定不同,React采用自上而下单向数据流的方式,管理自身的数据与状态。
在单向数据流中,数据只能由父组件触发,向下传递到子组件。

我们可以在父组件中定义state,并通过props的方式传递到子组件。
如果子组件想要修改父组件传递而来的状态,则只能给父组件发送消息,由父组件改变,再重新传递给子组件。

在React中,state与props的改变,都会引发组件重新渲染
如果是父组件的变化,则父组件下所有子组件都会重新渲染。

在class组件中,组件重新渲染,是执行render方法。
而在函数式组件中,是整个函数重新执行

函数式组件

函数式组件与普通的函数几乎完全一样。
只不过函数执行完毕时,返回的是一个JSX结构。


function Hello() {
  return <div>hello world.</div>
}

函数式组件非常简单,也正因如此,一些特性常常被忽略,而这些特性,是掌握React Hooks的关键。

  • 函数式组件接收props作为自己的参数
import React from 'react';

interface Props {
  name: string,
  age: number
}

function Demo({ name, age }: Props) {
  return [
    <div>name: {name}</div>,
    <div>age: {age}</div>
  ]
}

export default Demo;
  • props的每次变动,组件都会重新渲染一次,函数重新执行

  • 没有this。那么也就意味着,之前在class中由于this带来的困扰就自然消失了

useState 使用

  • useState Hook 让函数组件也可以有 state 状态,并进行读写操作
  • 语法:
const [state, setState] = React.useState(initalState)
// x代表可以自定义,X代表首字母大写
const [xxx, setXxx] = React.useState(initalValue)

  • useState()说明:

  • 参数:第一次初始化指定的值在内部作缓存

  • 返回值:包含两个元素的数组,第1个为内部当前状态值,第2个为更新状态值的函数

  • setState()的2种写法:

  • setState(newValue):参数为非函数值,直接指定新的状态值,内部用其覆盖原来的值

  • setState(value=>newValue):参数为函数,接收原本的状态值,返回新的状态值,内部用其覆盖原来的状态值。

setCount(count+1)
setCount(count => {return count+1})
- useState() 不帮助你处理状态,
- 相较于 setState() 非覆盖式更新状态,
- useState() 覆盖式更新状态,
- 需要开发者自己处理逻辑。

  • 用来举例的有内部状态的函数式组件

import React, { useState } from 'react';

export default function Counter() {
  const [counter, setCounter] = useState(0);
// 利用useState声明状态,每当点击时,setCounter执行,counter递增
  return (
    <div>
    	<div key="a">{counter}</div>,
    	// 当用户点击按钮后,我们传递一个新的值给 setCount。
    	// React 会重新渲染 Counter 组件,并把最新的 count 传给它。
    	<button key="b" onClick={() => setCounter(counter + 1)}>
      		点击+1
    	</button>
  	</div>
  )
}
  • 如果counter是一个引用类型
// counter默认值为 { a: 1, b: 2 }
const [counter, setCounter] = useState({ a: 1, b: 2 });

// 此时counter的值被改为了 { b: 4 }, 而不是 { a: 1, b: 4 }
setCounter({ b: 4 });

// 如果想要得到 { a: 1, b: 4 }的结果,就必须这样
setCounter({ ...counter, b: 4 });
  • 用下面的例子修改状态,会让组件重新渲染吗?
const [counter, setCounter] = useState({ a: 1, b: 2 });
// 修改counter的值
counter.b = 4;
setCounter(counter);
  • useState接收一个值作为当前定义的state的初始值。
  • 并且初始操作只有组件首次渲染才会执行。
// 首次执行,counter初始值为10
// 再次执行,因为在后面因为某种操作改变了counter,则获取到的便不再是初始值,而是闭包中的缓存值
const [counter, setCounter] = useState(10);
setCounter(20);
  • 如果初始值需要通过较为复杂的计算得出,则可以传入一个函数作为参数,函数返回值为初始值。
  • 该函数也只会在组件首次渲染时执行一次。
const a = 10;
const b = 20

// 初始值为a、b计算之和
const [counter, setCounter] = useState(() => {
  return a + b;
})

state 两者区别

function state 和 class state

  • 使用类组件
import React, { Component } from 'react';
//计数器
class App extends Component {
  constructor(props) {
    super(props);
    //需要使用 state保存组组件中需更新的数据
    this.state = { count: 0 }
  }
  changeCount = () => {
    //更新树需要调用 setState
    this.setState({
      count: this.state.count + 1
    })
  }
  render() {
    return (<div>
      <h2>计数器</h2>
      <button onClick={this.changeCount}>++</button>
      {this.state.count}
    </div>);
  }
}
export default App;
  • 在函数式组件中使用 useState
//useState 使用状态值,第一个 hook
//1.引入useState  钩子
import React, { useState } from 'react';
//计数器
export function AppNumber() {
  //2.设置state的初始值
  //useState函数的参数 可以设置state状态的初始值
  /*
  useState函数的返回值一个数组,数组里有两个元素,
  第一个元素 是 state的key
  第二个元素是 可以更改 state状态  的函数名 相当于  setState  
  */
  //注意:修改state的状态值的函数名 起名时  需要 使用 set开头
  const [count, setCount] = useState(5);
  const changeIncrementCount = () => {
    //3. 更改state的值
    setCount(count + 1);
  }
  return (
    <div>
      <h2>计数器</h2>
      count:{count}
      <button onClick={changeIncrementCount}>+</button>
    </div>
  )
}
//计数器-对象形式的state
export function AppObject() {
  // 定义对象形式的state
  const [obj, setObj] = useState({
    count: 10,
    name: "lili"
  })
  const changeCount = () => {
    setObj({ ...obj, count: obj.count + 1 })
  }
  return (
    <div>
      <h2>计数器</h2>
      count:{obj.count}
      <button onClick={changeCount}>+</button>
    </div>
  )
}

快照(闭包) vs 最新值(引用)

function state 保存的是快照,class state 保存的是最新值

  • 在这个例子中,3秒内连续点击5次,页面上的数字会从0增长到5。
import React, { Component } from 'react';
//计数器
class App extends Component {
  state = {
	count: 0
  }
  changeCount = () => {
    setTimeout(()=>{
		this.setState({
      		count: this.state.count + 1
    	})
    },3000)
  }
  render() {
    return (<div>
      <h2>计数器</h2>
      <button onClick={this.changeCount}>++</button>
      {this.state.count}
    </div>);
  }
}
export default App;
  • 在这个例子中,3秒内连续点击5次,页面上的数字会从0增长到1。
//useState 使用状态值,第一个 hook
//1.引入useState  钩子
import React, { useState } from 'react';
//计数器
function App() {
  const [count, setCount] = useState(0);
  const changeIncrementCount = () => {
    setTimeout(()=>{
		setCount(count + 1);
    },3000)
  }
  return (
    <div>
      <h2>计数器</h2>
      count:{count}
      <button onClick={changeIncrementCount}>+</button>
    </div>
  )
}
export default App;

分析:这主要是引用和闭包的区别

class 组件里面可以通过 this.state 引用到 count,
所以每次 setTimeout 的时候都能通过引用拿到上一次的最新 count,所以点击多少次最后就加了多少。

function 组件里面每次更新都是重新执行当前函数,
也就是说 setTimeout 里面读取到的 count 是通过闭包获取的,
而这个 count 实际上只是初始值,并不是上次执行完成后的最新值,所以最后只加了1次。

扩展:想要解决这个问题,那就涉及到另一个新的 Hook 方法 —— useRef。

  • 5
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 2
    评论
React Hooks 是 React 16.8 中新增的特性,它允许函数组件中使用 state 和其他 React 特性,从而使函数组件具有类组件的能力。 使用 React Hooks 需要先引入 ReactuseStateuseEffect 等钩子函数,然后在函数组件中使用它们。 useState useState 是最常用的 Hook 之一,它可以让我们在函数组件中使用 stateuseState 接收一个初始值作为参数,并返回一个数组,数组的第一个元素是当前 state 的值,第二个元素是更新 state 的函数。 例如,下面的代码在函数组件中使用了 useState 来保存一个计数器: ``` import React, { useState } from 'react'; function Counter() { const [count, setCount] = useState(0); return ( <div> <p>You clicked {count} times</p> <button onClick={() => setCount(count + 1)}> Click me </button> </div> ); } ``` useEffect useEffect 是另外一个常用的 Hook,它可以在函数组件中使用副作用。副作用包括数据获取、订阅或手动修改 DOM 等操作。useEffect 接收一个函数作为参数,该函数会在组件渲染完成后执行。 例如,下面的代码使用 useEffect 来更新页面标题: ``` import React, { useState, useEffect } from 'react'; function PageTitle() { const [count, setCount] = useState(0); useEffect(() => { document.title = `You clicked ${count} times`; }); return ( <div> <p>You clicked {count} times</p> <button onClick={() => setCount(count + 1)}> Click me </button> </div> ); } ``` useContext useContext 可以让我们在函数组件中使用 Context。Context 是一种在组件树中传递数据的方法,它可以避免通过 props 层层传递数据。 例如,下面的代码使用 useContext 来获取全局的主题: ``` import React, { useContext } from 'react'; const ThemeContext = React.createContext('light'); function ThemeButton() { const theme = useContext(ThemeContext); return ( <button style={{ background: theme.background, color: theme.foreground }}> I am styled by theme context! </button> ); } ``` 使用 useContext 前需要先创建一个 Context,可以使用 React.createContext 方法来创建。 除了上述三个 Hook,还有 useReducer、useCallback、useMemo、useRef 等 Hook 可以使用。使用这些 Hook 可以让函数组件更加强大和灵活。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

一颗不甘坠落的流星

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值