react中使用usestate踩坑及解决

目录
  • usestate的常规用法
  • useState遇到的坑
    • 1、useState不适合复杂对象的更改
    • 2、useState异步回调的问题
    • 3、根据hook的规则,使用useState的位置有限制
    • 4、使用useState,回调函数形式更改数据
    • 5、useState存入的值只是该值的引用(引用类型)
    • 6、useState,如果保存引用数据,useEffect检测不到变化?
    • 7、useState无法保存一个函数
  • useState实现原理

usestate的常规用法

在react框架中,不适用类组件,使用函数式组件又想自定义数据维护业务开发的时候,就需要使用react提供的hook来完成。usestate就是最常见的一种hook。

const [name,setName] = useState('dx');
setName('dx1')

中括号实际是一个解构运算,第一个name是设置的值,第二个setName是只能用来改变name的方法。

useState遇到的坑

1、useState不适合复杂对象的更改

因为useState不能像setState那样进行合并更新,当使用useState第二个参数进行数据更新的时候,必须传入一个完整的结构,而不仅仅只是改变的那一部分。

如果你想让一个复杂的对象都能实现响应,可以分两种情况。

第一种情况,这个复杂的对象每次都是整体发生改变,那么也可以直接使用useState。

第二种情况,你只是想让许多的简单数据都放到一个对象里面,这样便于统一管理,那我建议,如果这些简单数据之间都没什么必然联系的话,还是分开创建多个state更好。

在编码的过程中,我们宁愿以空间复杂度换取时间复杂度,多创建几个变量和创建一个变量,在用户体验上并不会有太多的差别。

但如果数据过于复杂,diff算法找到对应的变化及发生响应,大规模的重新渲染,这一过程,将会导致用户体验下降。

2、useState异步回调的问题

当使用usestate对数据进行更新,并不能立刻获取到最新的数据。

  const [name, setName] = useState('dx');

  const handleTest = () => {
    console.log(name) // dx
    setName('dx1')
    console.log(name) // dx
  }

解决的办法。

一、配合useEffect使用

  const [name, setName] = useState('dx');
  const handleTest = () => {
    console.log(name) //dx
    setName('dx1')
    console.log(name)//dx
  }
  
  useEffect(() => {
    console.log(name) //dx1
  },[name])

二、创建一个新的变量保存最新的数据

  const [name, setName] = useState('dx');
  const handleTest = () => {
    console.log(name) //dx
    const newName = "dx1"
    setName(newName)
    console.log(newName) //dx1
  }

三、用一个函数包裹,不推荐使用,因为函数里面所有的东西都会全部重新定义

const [name, setName] = useState('dx');
 function text () {
   const handleTest = () => {
     console.log(name) //dx
     const newName = "dx1"
     setName(newName)
     console.log(name) //dx
     console.log(newName) //dx1
   }
   useEffect(() => {
     console.log(name) //dx1
   },[name])

   return (
     <div>
       {name} //点击之前dx,点击按钮之后dx1
      <button type="button" onClick={handleTest }>改变名字</button>
     </div>
   )
 }
console.log(name) //点击按钮之前dx,点击按钮之后dx1

3、根据hook的规则,使用useState的位置有限制

强调,所有的hook和自定义hook都遵循此规则。

仅顶层调用 Hook :不能在循环,条件,嵌套函数等中调用useState()。

在多个useState()调用中,渲染之间的调用顺序必须相同。

仅从React 函数调用 Hook:必须仅在函数组件或自定义钩子内部调用useState()。

4、使用useState,回调函数形式更改数据

const [a, setA] = useState({c:1})
/** oldA为之前的a,return为设置的新值 */
setA((oldA) => {
return {c: oldA.c + 1}
})

5、useState存入的值只是该值的引用(引用类型)

const textObj = {name:'dx'}

const [useState1, setUseState1] = useState(textObj )

const [useState2, setUseState2] = useState(textObj )
/** usestate的操作不要放在函数的最外层,这里只是简单的代码展示,你可以将set操作放在某个函数里面 */
setUseState1((oldUseState1) => {
	oldUseState1.age = 18
return {...oldUseState1}
})

useEffect(() => {
	/** 改变一个会导致两个都改变,深浅拷贝的问题 */
	console.log(useState1)  // {name: "dx", age: 18}
	console.log(useState2)  // {name: "dx", age: 18}
},[
useState1
])

解决的方案

const textObj = {name:'dx'}

const [useState1, setUseState1] = useState(textObj )

const [useState2, setUseState2] = useState(JSON.parse(JSON.stringify(textObj)))
/** usestate的操作不要放在函数的最外层,这里只是简单的代码展示,你可以将set操作放在某个函数里面 */
setUseState1((oldUseState1) => {
	oldUseState1.age = 18
return {...oldUseState1}
})

useEffect(() => {
	/** 改变一个会导致两个都改变,深浅拷贝的问题 */
	console.log(useState1)  // {name: "dx", age: 18}
	console.log(useState2)  // {name: "dx"}
},[
useState1
])

6、useState,如果保存引用数据,useEffect检测不到变化?

const textObj = {name:'dx'}
const [useState1, setUseState1] = useState(textObj)
/** usestate的操作不要放在函数的最外层,这里只是简单的代码展示,你可以将set操作放在某个函数里面 */
setUseState1((oldUseState1) => {
	oldUseState1.age = 18
return oldUseState1

useEffect(() => {
	console.log(useState1)
},[
useState1
])
//结果是没有任何反应

解决方法

const textObj = {name:'dx'}
const [useState1, setUseState1] = useState(textObj)
/** usestate的操作不要放在函数的最外层,这里只是简单的代码展示,你可以将set操作放在某个函数里面 */
setUseState1((oldUseState1) => {
	oldUseState1.age = 18
	/** 返回一个新的对象,useEffectc才能检测得到 */
return {...oldUseState1}

useEffect(() => {
	console.log(useState1)  // {name: "dx", age: 18}
},[
useState1
])

7、useState无法保存一个函数

你是否尝试着将函数的引用作为一个变量保存到useState中去呢?

比如:

  const testFunciton1 = () => {
    console.log({name: 'dx',age: '18'})
  }

  /** usestate保存函数测试 */
  const [stateFunction, setstateFunction] = useState<() => void>(testFunciton1);

  useEffect(() => {
   console.log(stateFunction)
  }, [stateFunction])

打印结果

代码中从未调用过testFunciton1 ,结果testFunciton1却被执行了

useEffect打印出来的却是一个undefined。

稍微改动一下代码,再测试

  const testFunciton1 = () => {
    console.log({name: 'dx',age: '18'})
    return {
      name: 'yx',
      age: '17'
    }
  }

  /** usestate保存函数测试 */
  const [stateFunction, setstateFunction] = useState<() => void>(testFunciton1);

  useEffect(() => {
   console.log(stateFunction)
  }, [stateFunction])

结果

很明显,在useState中,函数会自动调用,并且保存函数返回的值,而不能保存函数本身。

解决的方案

使用useState不能保存函数,那么可以使用useCallback这个hook。

  /** useCallback,使用参数与useEffect一致 */
  const testFunction = useCallback(() => {
    // useCallback返回的函数在useCallbak中构建
    const testFunciton1 = () => {
      console.log({ name: 'dx', age: '18' });
      return {
        name: 'yx',
        age: '17',
      };
    };
    return testFunciton1;
  }, []);

  useEffect(() => {
    console.log(testFunction());
  }, [testFunction]);

结果

useState实现原理

前一段时间面试某大厂的时候,讲到了useState这个hook,要求简单写一下useState的实现原理,以下代码只是一个粗浅的原理。

function useState(init) {
	let state;
	// useState无法保存函数
	if(typeof init === 'function') {
		state = init()
	} else {
		state = init
	}

	const setState = (change) => {
		// 判断一下是否传递过来的是函数
		if(typeof change === 'function') {
			// 如果是函数,调用,并将之前的state传过去,接收到的返回值作为新的state并赋值
			state = change(state)
		} else {
			// 如果不是函数,直接赋值
			state = change;
		}
	}
	return [state, setState]
}

以上为个人经验,希望能给大家一个参考,也希望大家多多支持我们。

(0)

相关推荐

  • react中useState使用:如何实现在当前表格直接更改数据

    目录 如何实现在当前表格直接更改数据 需求 效果如下 具体做法 useState修改对象的字段 如何实现在当前表格直接更改数据 需求 用户点击修改按钮时直接在弹出框的当前页面内直接再次修改点击行相关信息: 效果如下 点击修改当事人信息时,直接将当前改为输入框,并将信息展示,同时操作栏内的内容变为保存和取消; 具体做法 我这里是使用的antd组件内的可编辑表格;当然原生的也可以做,以前也做过; 这里的关键是点击修改按钮时,令当前行的表格变为输入框,并展示数据; 给数据每一项加上 editable:

  • 解决react中useState状态异步更新的问题

    目录 疑惑 状态异步更新带来的问题 问题示例 问题解决 类组件的解决方案 函数组件的解决方案 其他解决方案 结尾 疑惑 相信刚开始使用react函数组件的小伙伴也遇到过一个坑,就是 useState 更新状态是异步更新的,但是react 并没有提供关于这个问题的解决方案.那我们能否使用自己的方法来解决这个问题呢?答案肯定是可以的. 状态异步更新带来的问题 就拿一个比较常见的场景来说.在react项目中,我们想在关闭对话框后再去处理其他业务.但是 useState 的状态是异步更新的.我们通过se

  • 示例详解react中useState的用法

    useState useState 通过在函数组件里调用它来给组件添加一些内部 state.React 会在重复渲染时保留这个 state.useState 会返回一对值:当前状态和一个让你更新它的函数,你可以在事件处理函数中或其他一些地方调用这个函数.它类似 class 组件的 this.setState,但是它不会把新的 state 和旧的 state 进行合并. 接下来通过一个示例来看看怎么使用 useState. 有这么一个需求:需要在 iframe 中加载外部网页. 初始的代码我们通过

  • react中使用usestate踩坑及解决

    目录 usestate的常规用法 useState遇到的坑 1.useState不适合复杂对象的更改 2.useState异步回调的问题 3.根据hook的规则,使用useState的位置有限制 4.使用useState,回调函数形式更改数据 5.useState存入的值只是该值的引用(引用类型) 6.useState,如果保存引用数据,useEffect检测不到变化? 7.useState无法保存一个函数 useState实现原理 usestate的常规用法 在react框架中,不适用类组件,

  • 基于IOS端微信分享失效的踩坑及解决方法

    最近的一个公众号是基于vue的spa应用,在接入微信分享和微信语音的时候出现了:在Android上一切正常,但是在ios端调用wx.config的时候总是失败,去翻了官方文档也并没有找到解决方案,最后在测试中发现是因为初始化的时候传入的URL的问题.具体过程如下: 微信config接口配置,官方文档如下: 所有需要使用JS-SDK的页面必须先注入配置信息,否则将无法调用(同一个url仅需调用一次,对于变化url的SPA的web app可在每次url变化时进行调用,目前Android微信客户端不支

  • 关于python scrapy中添加cookie踩坑记录

    问题发现: 前段时间项目中,为了防止被封号(提供的可用账号太少),对于能不登录就可以抓取的内容采用不带cookie的策略,只有必要的内容才带上cookie去访问. 本来想着很简单:在每个抛出来的Request的meta中带上一个标志位,通过在CookieMiddleware中查看这个标志位,决定是否是给这个Request是否装上Cookie. 实现的代码大致如下: class CookieMiddleware(object): """ 每次请求都随机从账号池中选择一个账号去访

  • springboot中Excel文件下载踩坑大全

    目录 项目场景:Spring boot文件下载 问题一:下载的文件名称出现中文乱码的问题 问题二:在swagger中测试下载接口,点击下载的文件,发现文件名是乱码的问题 问题四:开发环境下载成功,打成jar包发布到服务器上部署就出现下载失败问题 完整代码 项目场景:Spring boot文件下载 调用接口下载spring boot工程的resources目录下的excel模板文件,非常常见的一个文件下载功能,但是却容易遇到很多坑,下面总结记录下. 问题一:下载的文件名称出现中文乱码的问题 解决方

  • Java中Objects.equals踩坑记录

    目录 前言 1. 案发现场 2. 判断相等的方法 2.1 使用==号 2.2 使用equals方法 3. 空指针异常 4. Objects.equals的作用 5. Objects.equals的坑 总结 前言 最近review别人代码的时候,发现有个同事,在某个业务场景下,使用Objects.equals方法判断两个值相等时,返回了跟预期不一致的结果,引起了我的兴趣. 原本以为判断结果会返回true的,但实际上返回了false. 记得很早之前,我使用Objects.equals方法也踩过类似的

  • elementUI中MENU菜单踩坑

    需求:点击当前页面的按钮跳转到首页,给menu中绑定的default-active赋值 问题:页面已经跳转过去,可menu选中项根本没有发生变化 解决办法: 直接将当前页面的路由绑定到default-active上,同时将index改为当前路由,这样在通过非点击导航菜单跳转页面时就不需要再来手动改变导航菜单的选中项了,它会自己选中当前页面的tab项,完美解决! 说明:router属性很重要,default-active="this.$route.path"也很重要. <el-me

  • spring-cloud-gateway启动踩坑及解决

    目录 spring-cloud-gateway启动踩坑 1.webflux与mvc不兼容 2.webflux使用netty作为容器 3.后来实验了下 很坑得spring cloud gateway 异常 spring-cloud-gateway启动踩坑 本人使用的版本是2.1.2,以下只记录几个小问题,但确实实实在在的把个人恶心的要死要活的找不到办法,几经挣扎,最终解决. 更可恨的是开发的过程中,没有出现异常,后来由于项目组其它人加了依赖,不知不觉对项目的兼容造成了英雄,真的是被撞的头破血流,才

  • Spring数据库连接池url参数踩坑及解决

    目录 Spring数据库连接池url参数踩坑 遇到的问题 报错情况 解决 修改数据库连接池的url后,还是连接原先的url 问题 例如 Spring数据库连接池url参数踩坑 遇到的问题 报错情况 解决 & ' 字符在xml需要转义为 ' & ' 修改数据库连接池的url后,还是连接原先的url 问题 当修改连接池url之后,访问的还是原来的数据库. 例如 原来: url=jdbc:mysql://192.168.250.227:3306/myshop?characterEncoding=

  • nuxt使用vuex存储及获取用户信息踩坑的解决

    目录 一.背景 二.具体使用方法以及遇到的问题 三.解决办法 一.背景 按公司要求做一个电商网站,考虑到seo,所以用的是nuxt进行开发. 登录之后记录用户信息(user)使用cookie+vuex模式. 二.具体使用方法以及遇到的问题 1.无法解码导致无法转化为对象且不支持中文: 使用nuxt的nuxtServerInit在在服务端的cookie里获取用户信息,但是user在存储时使用js-cookie,客户端已对数据进行编码且可能包含中文,在使用JSON.parse进行string转化为对

  • springboot整合freemarker的踩坑及解决

    目录 springboot整合freemarker踩坑 报错 问题原因 解决方法 springboot freemarker基础配置及使用 1.基础配置 2.基础使用 springboot整合freemarker踩坑 报错 2021-04-23 02:01:18.148 ERROR 9484 --- [nio-8080-exec-1] o.a.c.c.C.[.[.[/].[dispatcherServlet]    : Servlet.service() for servlet [dispatc

随机推荐