函数组件中的函数应该去哪里?

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 上调用updatedraw

在大多数情况下,您应该在组件函数之外声明这些函数,以便只声明一次并始终重复使用相同的引用。当你在里面声明函数时,每次渲染组件时都会重新定义函数。

在某些情况下,您需要在组件内部定义一个函数,例如,将其分配为一个事件处理程序,该处理程序根据组件的属性表现不同。但是您仍然可以在 Ball 之外定义函数并将其与属性绑定,从而使代码更加简洁,并使 updatedraw 函数可重用。

// 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 函数有关。它使用父级的&lt;canvas&gt; 中的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 年的问题,并且回答的问题有很多赞成票。你可能不会得到你的投票。

以上是关于函数组件中的函数应该去哪里?的主要内容,如果未能解决你的问题,请参考以下文章

我应该在哪里绑定 React 组件中的方法?

在 joomla 组件中,我应该在哪里存储数据库函数,以及如何调用它们?

我应该在哪里绑定React组件中的方法?

RTOS 任务入口函数执行完之后去哪里了?

网络安全去应该去哪里学习呢?。

我们应该在线程类中的哪里编写原始线程函数