react创建自定义组件
In 2003, noted technologist Murphy Lee presaged our current state of modular component based web applications when he asked:
在2003年,著名的技术专家Murphy Lee在询问时预告了我们当前基于模块化组件的Web应用程序的状态:
Wat da hook gon be?
扫管hook钩坤是吗?
Though adamant that he himself had neither need nor want of hooks, many of the problems web developers face today cannot be solved simply with tracks in the background, their headphones loud and a blunt going round and they gonna rip.
尽管他坚持自己不需要钩子,但Web开发人员如今面临的许多问题无法简单地通过背景音轨,耳机响亮而钝钝的声音来解决。
Since the introduction of Hooks in late 2018, they have become a vital part of the React ecosystem, and no wonder, they’re great.
自2018年底引入Hooks以来,它们已成为React生态系统的重要组成部分,难怪它们很棒。
但是什么是钩子? (But what is a hook?)
A hook can be many things.
钩子可以是很多东西。
Basic hooks provided by React allow you to use stateful logic and lifecycle functions in function components, which prior to their introduction was only available in class based components. There are hooks that allow you to chain callbacks, memoize functions and objects and communicate directly with a ref to the DOM.
React提供的基本挂钩允许您在功能组件中使用状态逻辑和生命周期函数,而在引入它们之前,仅在基于类的组件中可用。 有一些钩子使您可以链接回调,记住函数和对象并直接与对DOM的引用进行通信。
And that’s only the hooks provided out of the box by React. Where hooks really start to shine is when you build them out. There’s a good chance that any piece of lower level logic you want to reuse can be expressed as a custom hook. The React community knows this and has created many custom hooks that you can use right now.
这只是React提供的开箱即用的钩子。 钩子真正开始发光的地方是当您将钩子扩大时。 您要重用的任何较低层逻辑很可能都可以表示为自定义钩子。 React社区知道这一点,并创建了许多您现在可以使用的自定义钩子。
Accessing localStorage
?
访问localStorage
?
Wat Da Hook Gon Be?
达胡寺(Wat Da Hook Gon Be)?
Syncing with the indexDB
api?
与indexDB
API同步?
Wat Da Hook Gon Be?
达胡寺(Wat Da Hook Gon Be)?
Even something as simple as seeing whether or not your computer is online.
甚至只是查看计算机是否在线的简单操作。
Wat. Da. Hook. Gon. Be?
笏。 大。 钩。 刚。 是?
听起来不错! 那么,下载一堆钩子吧? (Sounds great! So, download a bunch of hooks right?)
Well, you can. And if you’re pressed for time that would be my advice. The above hooks and many more like them work well, are tested and should help you solve whatever problem you’re up against.
好吧,可以。 如果您时间紧迫,那将是我的建议。 上面的钩子以及其他类似的钩子都可以正常工作,已经过测试,可以帮助您解决遇到的任何问题。
But that won’ t help you learn how they’re working.
但是,韩元”吨帮助您了解他们是如何工作。
That’s why today I’m going to work with you and help you build your own reusable hook to create draggable components.
这就是为什么今天我将与您合作,并帮助您构建自己的可重用挂钩以创建可拖动组件的原因。
You can find the complete code on GitHub.
您可以在GitHub上找到完整的代码。
1.准备和安装 (1. Prep and Install)
Enter npx create-react-app wat_da_hook_gon_be
into your terminal to get started.
在您的终端中输入npx create-react-app wat_da_hook_gon_be
开始使用。
Download the 200 x 200 px image above and save it to your src
folder and rename it to albumcover.jpg
, then copy the below text to your App
file.
下载上面的200 x 200 px图像并将其保存到您的src
文件夹中,并将其重命名为albumcover.jpg
,然后将以下文本复制到您的App
文件中。
If you input yarn start
into your terminal you should see the picture in the upper left hand corner of your browser.
如果在终端中输入yarn start
,则应该在浏览器的左上角看到图片。
2.添加起始位置和状态 (2. Add starting position and state)
There will be quite a bit going on in the hook we’re building. So, for both of our benefits, I’m going to break things down into a few steps and explain what I’m doing on the way.
我们正在构建的钩子中会有很多事情要做。 因此,为了我们的两个好处,我将把事情分解为几个步骤,并解释我在路上所做的事情。
I know that when I am learning new concepts and techniques, I prefer a slow, step by step explanation. So I’m going to break it down as simply as I can.
我知道,当我学习新的概念和技术时,我希望逐步进行逐步解释。 因此,我将尽可能简单地分解它。
Modify App.js
with the following code:
使用以下代码修改App.js
:
Let’s break down what’s going on here.
让我们分解一下这里发生的事情。
At the top in our first import, we are importing the useState
hook.
在第一个导入的顶部,我们将导入useState
挂钩。
useState
is the workhorse of functional components in React. It accepts one argument, an initial state to use, and returns two values: a variable to represent the piece of state and a function to update it. Upon using the function to update state, a rerender is triggered and anything subscribed to the state variable is updated.
useState
是React中功能组件的主力军。 它接受一个参数,要使用的初始状态,并返回两个值:代表状态的变量和更新状态的函数。 使用该功能更新状态时,将触发重新渲染,并更新订阅状态变量的所有内容。
After the inline styles that we set up in our last step, we are destructuring the innerHeight
and innerWidth
properties from the global window
object.
在最后一步中设置了内联样式之后,我们正在从全局window
对象中破坏innerHeight
和innerWidth
属性。
In the next line, we put those destructured values to work setting what will be our initial position value. We divide them by half and then subtract 100 from that. As our image is 200 by 200 pixels, this will set the middle of our picture to the middle of our browser.
在下一行中,我们将那些解构后的值用于设置初始位置值。 我们将它们除以一半,然后从中减去100。 由于我们的图片为200 x 200像素,因此会将图片的中间位置设置为浏览器的中间位置。
Next we have the initial value for useState
. First, we have the boolean value isDragging
set to false.
This will be used to tell whether or not a component is being actively dragged or not. The next three properties will always be plain objects with x and y values in them. These represent the x and y axes of our screen, in pixels. We’ll start with origin
at x : 0 and y : 0. translation
and lastTranslation
, which we will talk more about in a bit, will start at the value set in our startingPosition
variable.
接下来,我们有了useState
的初始值。 首先,我们将布尔值isDragging
设置为false.
这将用于指示组件是否正在被主动拖动。 接下来的三个属性将始终是其中包含x和y值的普通对象。 这些代表我们的屏幕的x和y轴(以像素为单位)。 我们将从x:0和y:0的origin
开始。我们将在稍后讨论的translation
和lastTranslation
将从startingPosition
变量中设置的值startingPosition
。
Nothing has changed on the screen just yet, but we are well on our way.
屏幕上尚未发生任何变化,但我们的工作进展顺利。
3.添加handleMouseDown (3. Add handleMouseDown)
A very common pattern when dealing with mouse events is to break down the logic into three functions, handling logic for when a mouse button is clicked, when the mouse is moved and when the mouse button is released. We will be utilizing this pattern with three functions, called handleMouseDown
, handleMouseMove
, and handleMouseUp
.
处理鼠标事件时,一种非常常见的模式是将逻辑分为三个功能:处理单击鼠标按钮,移动鼠标和释放鼠标按钮的逻辑。 我们将通过三个函数来使用此模式,这三个函数分别是handleMouseDown
, handleMouseMove
和handleMouseUp
。
Observe, the following code.
观察下面的代码。
Before we start, we’re going to destructure isDragging
from our dragInfo
state variable to make things just a bit easier to work with.
在开始之前,我们将从我们的dragInfo
状态变量中解构isDragging
,以使事情更容易使用。
The handleMouseDown
function will accept two arguments, clientX
and clientY
, destructured from a fired mouse event. These are the coordinates on the X and Y axes at the time we click on the element we would like to drag. Not too complex right?
handleMouseDown
函数将接受两个参数, clientX
从触发的鼠标事件解构的clientX
和clientY
。 这些是我们单击要拖动的元素时X和Y轴上的坐标。 不太复杂吧?
If isDragging
is set to false, we will proceed into our function. We will call our setDragInfo
method to update our dragInfo
state, changing isDragging
to true
and our origin
coordinates to clientX
and clientY
.
如果isDragging
设置为false,我们将进入函数。 我们将调用setDragInfo
方法来更新dragInfo
状态,将isDragging
更改为true
并将origin
坐标更改为clientX
和clientY
。
4.添加handleMouseMove (4. Add handleMouseMove)
The handleMouseMove
function will fire again and again whenever our mouse is moved, for however long we hold it down. So, I think we should try to get it right, don’t you?
每当我们移动鼠标时, handleMouseMove
函数都会一次又一次触发,无论按住多长时间。 所以,我认为我们应该尽力做到正确,不是吗?
Behold!
看哪!
Like our last function, handleMouseMove
takes the destructured clientX
and clientY
properties as arguments. If isDragging
resolves to true
, the origin
and lastTranslation
values are destructured from draginfo
and we again call our setDragInfo
action to update our state. Do you see how this is really just setting up rapid fire updates to state again and again?
像我们的最后一个函数一样, handleMouseMove
将经过解构的clientX
和clientY
属性作为参数。 如果isDragging
解析为true
,那么origin
和lastTranslation
值将从draginfo
然后再次调用setDragInfo
操作以更新状态。 您是否看到这真的只是在设置快速更新以反复陈述状态?
The only value we’re interested in changing right now istranslation
. We’ll add the origin
values we stored during our mouseDown
function, add them to our last translation value and subtract that from the client
argument values fired from the mouse event. To avoid getting negative values we’ll wrap that whole equation in Math.abs
. This calls the absolute value, so only positive values will return from this function, and therefore it will stay on the screen.
现在,我们唯一想改变的价值是translation
。 我们将添加在mouseDown
函数期间存储的origin
值,将它们添加到最后的转换值,然后从mouse事件中触发的client
参数值中减去这些origin
值。 为了避免得到负值,我们将整个方程式包装在Math.abs
。 这将调用绝对值,因此此函数将仅返回正值,因此它将保留在屏幕上。
In the next step we’ll be attaching the translation
value directly to CSS values on our draggable object, updating them on every render.
下一步,我们将translation
值直接附加到可拖动对象上CSS值上,并在每个渲染器上对其进行更新。
5. handleMouseUp并将其捆绑在一起 (5. handleMouseUp and tying it all together)
For our last and final function, we will not be needing any arguments, as all we’re going to do is turn off our dragging and set a value to be used for our next drag.
对于我们最后的遗愿功能,我们会不会需要任何参数,如我们所要做的是关掉我们的拖动,并设置用于我们的下一个阻力值。
To do this, once again if our isDragging
variable resolves to true
, we will destructure translation
from dragInfo
. Remember that this is the value we just changed many, many times during our handleMouseMove
function. Again, calling our setDragInfo
method, we will change isDragging
to false
, ending the drag cycle, set the lastTranslation
value to the last returned value of translation
from handleMouseMove
and spread the rest.
为此,如果isDragging
变量解析为true
,我们将再次从dragInfo
解构translation
。 请记住,这是我们在handleMouseMove
函数中多次更改的值。 同样,拨打我们的setDragInfo
方法,我们将改变isDragging
到false
,结束拖周期,设置lastTranslation
值的最后一个返回值translation
从handleMouseMove
和传播休息。
Yes! It really is that simple. Pretty much it’s just updating x and y values with the mouse. But before it will work, we’ll need to attach all those to our elements somehow. We do this with the inline style variable picturePosition
. It changes our position
rule to absolute
and attaches the right
and bottom
rules to the x and y values of translation
. Since it is generated with every render, it will generate a fresh position for our dragged item as we drag it.
是! 真的就是这么简单。 差不多只是用鼠标更新x和y值。 但是在它起作用之前,我们需要以某种方式将所有这些附加到我们的元素上。 我们使用内联样式变量picturePosition
进行此picturePosition
。 它将position
规则更改为absolute
规则,并在translation
的x和y值上附加了right
规则和bottom
规则。 由于它是在每个渲染器中生成的,因此在我们拖动项目时,它将为我们拖动的项目生成一个新位置。
After that, it’s a simple matter of spreading the inline style objects in picturePosition
and pictureStyle
and attaching mouse events to the appropriate synthetic event listeners on the element we wish to be dragged.
之后,只需将内联样式对象分布在picturePosition
和pictureStyle
然后将鼠标事件附加到我们希望拖动的元素上的适当合成事件侦听器上,就很简单。
But wait a minute, why are there two events attached to handleMouseMove
? When the mouse leaves the element, don’t we want it to, you know, leave?
但是请稍等,为什么handleMouseMove
附加两个事件? 当鼠标离开元素时,我们不希望它离开吗?
Try using this code without that listener. It will work, mostly. The problem is when you move the pointer too fast, often the mouse will move faster than the functions can keep up with and you will lose the drag. Or, you won’t lose the drag but other strange behaviors may happen, like no longer being able to hover over the element. Attaching handleMouseMove
to both onMouseMove
and onMouseLeave
eliminates this headache. And since we’re using a boolean to detect whether a drag is happening, we don’t have to worry about a drag state getting stuck into a permanent ‘on’ position.
尝试在没有该侦听器的情况下使用此代码。 它将大部分起作用。 问题是,当您将指针移动得太快时,鼠标移动的速度通常会超过功能所能跟上的速度,并且会丢失拖动。 或者,您将不会失去阻力,但是可能会发生其他奇怪的行为,例如不再能够将鼠标悬停在元素上。 将handleMouseMove
附加到onMouseMove
和onMouseLeave
消除这种麻烦。 而且,由于我们使用布尔值来检测是否发生拖动,因此我们不必担心拖动状态会卡在永久的“打开”位置。
And that’s it! One draggable component attached to one element!
就是这样! 将一个可拖动组件附加到一个元素上!
It’s not very modular though. In our next step we’ll see where hooks really shine and abstract it out into a reusable useDrag
hook
虽然不是很模块化。 在下一步中,我们将看到钩子真正发光的地方,并将其抽象为可重复使用的useDrag
钩子。
6. useDrag (6. useDrag)
Create a new file in your src
folder called useDrag
and move the following code into it.
在src
文件夹中创建一个名为useDrag
的新文件,并将以下代码移入其中。
Here we’ve taken all the logic around dragging out our component, and put it into a place where we can reuse it again and again whenever we need it.
在这里,我们采用了拖出组件的所有逻辑,并将其放到一个可以在需要时重复使用的地方。
After you’ve done that, you can change App
to the following.
完成此操作后,可以将App
更改为以下内容。
最后的想法 (Final Thoughts)
Today we’ve how to make any element draggable with a simple three function process to handle mouse down events, mouse move events and mouse up events. We’ve also learned how to abstract that pattern into a reusable hook, so we can utilize it for any component we need. I hope this article has been enjoyable and informative for you, and you enjoyed reading it as much as I did writing it.
今天,我们已经通过一个简单的三功能过程使任何元素都可拖动,以处理鼠标按下事件,鼠标移动事件和鼠标按下事件。 我们还学习了如何将该模式抽象为可重用的钩子,以便可以将其用于所需的任何组件。 我希望本文对您来说是愉快和有益的,并且您喜欢阅读它,就像我写它一样。
react创建自定义组件