React使用hook如何实现数据双向绑定
作者:在听风吟
React使用hook实现数据双向绑定
写在前面:阅读hook的使用前,要对Class实现方法有一定了解,我们也从这里开始讲起:
const root = ReactDOM.createRoot(document.getElementById('root')); function tick() { const element = ( <div> <h1>Hello, world!</h1> <h2>It is {new Date().toLocaleTimeString()}.</h2> </div> ); root.render(element);} setInterval(tick, 1000);
在react初级,我们学会了使用ReactDOM进行元素的挂载、使用jsx模式编辑可视化更强的(<html />)、通过setInterval实现function的调用;如何使用Class进行更加规范性和可编辑性的代码书写呢?
在React官方文档有如下的步骤:
- 创建一个同名的 ES6 class,并且继承于
React.Component
。 - 添加一个空的
render()
方法。 - 将函数体移动到
render()
方法之中。 - 在
render()
方法中使用this.props
替换props
。 - 删除剩余的空函数声明。
首先,应用Class在我看来是一个必不可少的策略,因为原始的js编辑模式和引入react,使用react DOM进行挂载的方式过分原始,并且缺少作用域的划分,缺少可复用性,甚至在你想要多实现一个功能时变得很被动。
class Clock extends React.Component { render() { return ( <div> <h1>Hello, world!</h1> <h2>It is {this.props.date.toLocaleTimeString()}.</h2> </div> ); } } function tick() { ReactDOM.render( <Clock date={new Date()} />, document.getElementById('root') ); } setInterval(tick, 1000);
哪怕我们没有对Java足够熟悉,也并不清楚js中类和继承的底层原理,但是直观地,class的出现,让我们很快的想到一个词,那就是“面向对象”。
众所周知的时,JS并非面向对象的嫡子,而是在发展后期不断通过新的贡献而看上去(或者说使用上)更加的面向对象,而作为开发程序的工程师,尤其是遇“JS”不久的小白而言,使用是优于理解的,因为只有实践,才是掌握理论的不二法门。
接下来,我们通过一段相同效果的代码来说Class的问题和hook的使用:
import React, { useState } from 'react'; function Example() { // 声明一个叫 "count" 的 state 变量 const [count, setCount] = useState(0); return ( <div> <p>You clicked {count} times</p> <button onClick={() => setCount(count + 1)}> Click me </button> </div> ); }
class Example extends React.Component { constructor(props) { super(props); this.state = { count: 0 }; } render() { return ( <div> <p>You clicked {this.state.count} times</p> <button onClick={() => this.setState({ count: this.state.count + 1 })}> Click me </button> </div> ); } }
诚然,代码行数的减少可以使我们显得更加专业和高级,function的声明也似乎比class extends React.Component 云云更精简,不过真正值得注意的不仅仅于此:
关于this:
有过前端面试经验的小伙伴都清楚,this使用不当是一件很不好的事情,如果你想学好this,可以到掘金刷刷前端经典面试题,或者没事的时候打开Chrome的F12来动手试试,不过在你尚未吃透这个原理的所有场景,或者你单纯就是个谨慎的程序开发者,那么避免在一些可能出错的地方使用this将会是一个很好的选择,Hook的操作最大优点由此显示:通过前端开发的同学们更加熟悉的function写法,避免在各层调用中this指向不准而导致bug的出现;除此之外,通过count, setCount的绑定方法分离state和action层,const阻止变量污染,以上,是我想要分享的hook的优势,接下来将介绍hook的使用方法:useState, useEffect。
(新内容)
关于useState
基于上述内容,我们已经清楚hook的声明方式:
const [count, setCount] = useState(0); // 0是初始值
我们从这条语句开始说起,const是ES6中对于不可改变的量的声明方式,我们可以清晰地得到“不可以在程序中直接修改count的值"的字面意思,[ ]只是形式不去管他,count和setCount由自己定义,我们习惯于将前面的变量加上set的名称作为其修改方法,useState固定写法,( 0 )是初始值,可以传入js中有实际意义的各类对象,例如对象{}数组[]时间new Date或者一个简单的null。
关于set方法
第一次接触的同学会有疑问?如何使用修改方法呢,很简单,在相同的上下文内,直接使用setCount(*新的值*),“那上下文...." 还是转化成白话文吧:在当下组件可以直接使用,把useState声明的值当作当前组件的私有变量,如果需要在其他地方使用,请学习如何传值(这是另外一个话题,也可能在接下来一段时间更新到后续文章);
学习到此,我们大概会觉得这个方法好极了,不会被污染,自己私域可用,写法规范,可以绑定到DOM上,等等,记得开始时候我们说得是双向绑定,set方法对应的应该有一个get才对,那么hook的使用方法是什么呢?
异步与副作用:
首先来看一段不简单的代码:
const [count, setCount] = useState(0); setCount(1); console.log(count); // 控制台输出:0
好学的你一定知道这是setCount方法的异步性,官方文档给的解释叫做:“useState可能是个异步操作”,如何理解可能不重要,我们不可能将程序置于可能当中,于是官方提供了useState的兄弟方法:useEffect。
关于useEffect
const [count, setCount] = useState(0); setCount(1); console.log(count); // 控制台输出:0 useEffect(() => { console.log(count); // 控制台输出:1 });
这段代码不做解释,接着来讲:
熟悉vue的同学在开发过程中应当会应用到computed和watch属性,使得页面的数据保持“时时正确”性,很多情况,一个按钮会导致多个展示项的刷新,而延误是我们应当避免的,useEffect就是这样一个方法,他会在react内部启动监听,当某个对象发生改变,触发响应的函数,在这个函数中我们可以使用上述的useState所声明的set方法改变某个绑定到页面的数据项从而渲染新的页面。
举手:你刚刚讲的“某个对象”是怎么一回事?
请看这段代码:
useEffect(() => { document.title = `You clicked ${count} times`; }, [count]); // 仅在 count 更改时更新
OK,当你阅读到这个位置,就已经有相当深入的“入门“知识了,你可以选择新建一个react项目,随便输出几条内容,绑定一两个数据,实践出真知。
接着看代码,我们通常——或者说被规定——使用useEffect(function(), props);的方式来绑定useEffect的监视对象,在上面的例子中,count的值发生改变(by any reason)就会随之进行标题修改的语句。
OK,那么props既然是数组,我们可以传入多条监视源么?No。
使用Effect方法可以解决useState的异步问题么?Yes。
使用Effect方法能实现声明周期函数么?是的,我们将props的内容填空,即[],就会在组件创建之初进行一次且仅一次的调用,类似于Vue中的created声明周期钩子。
useEffect方法有异步性么?无关紧要,因为页面会随着useEffect中的语句更新。
useEffect的实现方式是什么样的?useEffect因为可以监测多个数据源,故一个组件会存在多个use Effect函数,他们会被当做一个整体,也就是一颗方法树,每个新的方法都会挂载在上面,在发生改变之后,整颗树一块刷新,并对应DOM树进行改变。
关于Hook额外的信息
不要在循环,条件或嵌套函数中调用 Hook, 确保总是在你的 React 函数的最顶层以及任何 return 之前调用他们。
遵守这条规则,你就能确保 Hook 在每一次渲染中都按照同样的顺序被调用。
这让 React 能够在多次的 useState
和 useEffect
调用之间保持 hook 状态的正确。(如果你对此感到好奇,我们在下面会有更深入的解释。)
不要在非react代码中使用Hook。
总结
(以上内容类似与考试须知,请大家自行理解)
以上为个人经验,希望能给大家一个参考,也希望大家多多支持脚本之家。