函数组件中的函数应该去哪里?
Posted
技术标签:
【中文标题】函数组件中的函数应该去哪里?【英文标题】:Where should functions in function components go? 【发布时间】:2018-02-18 15:49:07 【问题描述】:我正在尝试将我发现的 here 这个很酷的 <canvas>
动画转换为 React 可重用组件。看起来这个组件需要一个父组件用于画布,而许多子组件用于function Ball()
。
出于性能原因,将Balls
制作成无状态组件可能会更好,因为它们中会有很多。我对制作无状态组件不太熟悉,想知道应该在哪里定义function Ball()
中定义的this.update()
和this.draw
函数。
无状态组件的函数是在组件内部还是外部?换句话说,以下哪个更好?
1:
const Ball = (props) =>
const update = () =>
...
const draw = () =>
...
return (
...
);
2:
function update()
...
function draw()
...
const Ball = (props) =>
return (
...
);
每种方法的优缺点是什么,其中一种更适合我的特定用例?
【问题讨论】:
您能否发布现有代码,以便我们了解如何使用它? @Scimonster 我将它发布在嵌入式链接中,也许你错过了它。这是链接:codepen.io/awendland/pen/XJExGv 【参考方案1】:首先要注意的是,无状态功能组件不能有方法,如果它是无状态功能组件,则不应指望在渲染的Ball
上调用update
或draw
。
在大多数情况下,您应该在组件函数之外声明这些函数,以便只声明一次并始终重复使用相同的引用。当你在里面声明函数时,每次渲染组件时都会重新定义函数。
在某些情况下,您需要在组件内部定义一个函数,例如,将其分配为一个事件处理程序,该处理程序根据组件的属性表现不同。但是您仍然可以在 Ball
之外定义函数并将其与属性绑定,从而使代码更加简洁,并使 update
或 draw
函数可重用。
// You can use update somewhere else
const update (propX, a, b) => ... ;
const Ball = props => (
<Something onClick=update.bind(null, props.x) />
);
如果您使用钩子,您可以使用useCallback
确保仅在其依赖项之一(本例中为props.x
)发生更改时重新定义函数:
const Ball = props =>
const onClick = useCallback((a, b) =>
// do something with a, b and props.x
, [props.x]);
return (
<Something onClick=onClick />
);
这是错误的方式:
const Ball = props =>
function update(a, b)
// props.x is visible here
return (
<Something onClick=update />
);
当使用useCallback
时,在useCallback
钩子本身中定义update
函数在我们的组件外部成为最重要的设计决策,您应该考虑是否要重用update
和/或者如果你需要访问组件闭包的范围,例如,读/写状态。我个人选择默认在组件内部定义它,并使其仅在需要时可重用,以防止从一开始就过度设计。除此之外,重用应用程序逻辑最好使用更具体的钩子来完成,而留下组件用于演示目的。在使用 hooks 时定义组件外部的函数实际上取决于您希望应用程序逻辑与 React 的解耦等级。
【讨论】:
谢谢 Marco,这让事情变得更清楚了。在我的情况下,我感到困惑的事情与Ball
中的this.draw
函数有关。它使用父级的<canvas>
中的ctx
,并且还使用this
关键字作为子级Ball
组件。集成实现无状态组件以使这两个属性都可以访问的最佳方法是什么?
在使用无状态功能组件时没有this
,请记住这一点。对于画布上下文,您必须将其传递给每一个 Ball
,这听起来一点都不好。
@MarcoScabbiolo 不不,这不是我的情况,已经在本地使用箭头函数很长一段时间了,因为唯一不支持它们的浏览器是 IE。实际上,我设法从 one article 找到了这条评论,实际上它声称 bind
特别是在 Chrome 早于 59 中甚至比箭头函数慢。在 Firefox 中也需要很长一段时间,因为它们都以相同的速度运行。所以我会说在这种情况下,首选方式是什么没有区别:)
@MauricioAvendaño 任何一种方式都有效,但 Something
组件知道其父组件中有一个 prop X 是一种不好的做法,因为它会使其知道其上下文。您提出的问题和我编写的示例代码也是如此,这取决于上下文,为了简单起见,将其忽略。
@Atif 取决于组件及其子组件。了解原因:reactjs.org/docs/reconciliation.html 衡量它:reactjs.org/docs/optimizing-performance.html【参考方案2】:
您可以将函数放置在无状态功能组件中:
function Action()
function handlePick()
alert("test");
return (
<div>
<input type="button" onClick=handlePick value="What you want to do ?" />
</div>
)
但这不是一个好习惯,因为每次渲染组件时都会定义函数handlePick()
。
最好在组件外定义函数:
function handlePick()
alert("test");
function Action()
return (
<div>
<input type="button" onClick=handlePick value="What you want to do ?" />
</div>
)
【讨论】:
【参考方案3】:如果你想在函数中使用组件的 props 或 state,应该在组件中使用 useCallback 定义。
function Component(props)
const onClick=useCallback(()=>
// Do some things with props or state
,[])
return <Something ...onClick />
另一方面,如果您不想在函数中使用道具或状态,请在组件之外定义。
const computeSomethings=()=>
// Do some things with params or side effects
function Component(props)
return <Something onClick=computeSomethings />
对于 HTML 标签,你不需要 useCallback,因为它会在反应端处理并且不会分配给 HTML
function Component(props)
const onClick=()=>
// Do some things with props or state
return <Something ...onClick />
编辑:钩子中的函数
对于 hooks 中的 use 函数,例如 useEffect,我的建议是在 useEffect 中定义函数,如果您担心 DRY,请让您的函数在 hook 中纯调用它并给它您的参数。 钩子部门呢?您应该/可以将所有参数添加到 hooks deps,但 useEffect 只需要 deps 会影响它们的更改。
【讨论】:
你能举一个在功能组件内部使用钩子方法的例子吗? (不设置状态方法)【参考方案4】:我们可以在功能组件中使用 React hook useCallback
,如下所示:
const home = (props) =>
const small, img = props
const [currentInd, setCurrentInd] = useState(0);
const imgArrayLength = img.length - 1;
useEffect(() =>
let id = setInterval(() =>
if (currentInd < imgArrayLength)
setCurrentInd(currentInd => currentInd + 1)
else
setCurrentInd(0)
, 5000);
return () => clearInterval(id);
, [currentInd]);
const onLeftClickHandler = useCallback(
() =>
if (currentInd === 0)
else
setCurrentInd(currentInd => currentInd - 1)
,
[currentInd],
);
const onRightClickHandler = useCallback(
() =>
if (currentInd < imgArrayLength)
setCurrentInd(currentInd => currentInd + 1)
else
,
[currentInd],
);
return (
<Wrapper img=img[currentInd]>
<LeftSliderArrow className=currentInd > 0 ? "red" : 'no-red' onClick=onLeftClickHandler>
<img src=Icon_dir + "chevron_left_light.png"></img>
</LeftSliderArrow>
<RightSliderArrow className=currentInd < imgArrayLength ? "red" : 'no-red' onClick=onRightClickHandler>
<img src=Icon_dir + "chevron_right_light.png"></img>
</RightSliderArrow>
</Wrapper>);
export default home;
我从它的父母那里得到'img',那是一个数组。
【讨论】:
谢谢 - 我感觉有一个钩子,以便我们可以列出函数组件中const
函数的依赖关系!
UseCallback 只对记忆函数有用,因此在每次渲染时,函数都可以自己记忆,这需要一个 props 或 state 的依赖关系。【参考方案5】:
import React, useState from 'react';
function Example()
// Declare a new state variable, which we'll call "count"
const [count, setCount] = useState(0);
const a = () =>
setCount(count + 1);
;
return (
<div>
<p>You clicked count times</p>
<button onClick=a>Click me</button>
</div>
);
export default Example;
【讨论】:
我建议不要回答已经存在 3 年的问题,并且回答的问题有很多赞成票。你可能不会得到你的投票。以上是关于函数组件中的函数应该去哪里?的主要内容,如果未能解决你的问题,请参考以下文章