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

IT技术 javascript reactjs function
2021-02-28 00:04:33

我正在尝试将<canvas>我在此处找到的这个很酷的动画转换为 React 可重用组件。看起来这个组件需要一个用于画布的父组件,以及许多用于function Ball().

出于性能原因,将它们Balls变成无状态组件可能会更好,因为它们将有很多。我不熟悉使得无状态组件,并想知道我应该在哪里定义this.update()this.draw定义函数function Ball()

无状态组件的函数是放在组件内部还是外部?换句话说,以下哪个更好?

1:

const Ball = (props) => {
    const update = () => {
        ...
    }

    const draw = () => {
        ...
    }

    return (
       ...
    );
}

2:

function update() {
     ...
}

function draw() {
     ...
}

const Ball = (props) => {
    return (
       ...
    );
}

每种方法的优缺点是什么,对于特定用例(例如我的用例)来说,其中一种更好吗?

5个回答

首先要注意的是,无状态功能组件不能有方法,如果它是无状态功能组件,则不应指望调用updatedraw渲染Ball

在大多数情况下,您应该在组件函数之外声明函数,以便您只声明一次并始终重用相同的引用。当您在内部声明函数时,每次渲染组件时都会再次定义该函数。

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

// You can use update somewhere else
const update (propX, a, b) => { ... };

const Ball = props => (
  <Something onClick={update.bind(null, props.x)} />
);

如果您使用hooks,您可以使用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在组件外部定义钩子本身update函数useCallback比任何事情都更重要,您应该考虑是否要重用update和/或是否需要访问组件闭包的范围,例如,读/写状态。我个人选择默认在组件内部定义它,并仅在需要时才使其可重用,以防止从一开始就过度设计。最重要的是,重用应用程序逻辑最好使用更具体的钩子来完成,将组件留作展示用途。在使用钩子的同时在组件外部定义函数实际上取决于您希望应用程序逻辑与 React 的解耦等级。

没有this使用无状态的功能组件时,必须考虑到这一点。对于画布上下文,您必须将其传递给每一个Ball,这听起来一点儿都不好。
2021-04-27 00:04:33
谢谢 Marco,这让事情变得更清楚了一点。我在我的情况感到困惑的事情是相关的this.draw内部功能Ball它使用ctx来自父组件的内容<canvas>,并使用this关键字表示子Ball组件。集成实现无状态组件以便这两个属性都可以访问的最佳方法是什么?
2021-04-29 00:04:33
@MauricioAvendaño 无论哪种方式都有效,但是Something组件知道其父组件中有一个 prop X是一种不好的做法,因为它会使其了解其上下文。您提出的问题和我编写的示例代码也会发生同样的情况,这取决于上下文,为了简单起见,将其忽略。
2021-05-08 00:04:33
@MarcoScabbiolo 不不,这不是我的情况,已经在本地使用箭头函数很长时间了,因为唯一不支持它们的浏览器是 IE。实际上,我设法从一篇文章找到了这条评论,其中实际上声称bind59 之前的 Chrome中甚至比箭头函数还要而在 Firefox 中,这也是相当长的一段时间,因为它们都以相同的速度运行。所以我会说在这种情况下,首选方式没有区别:)
2021-05-10 00:04:33
@Atif 取决于组件及其子组件。了解原因:reactjs.org/docs/reconciliation.html衡量它:reactjs.org/docs/optimizing-performance.html
2021-05-15 00:04:33

您可以将函数放置在无状态函数组件中:

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>
    )
}

如果要在函数中使用 props 或组件状态,则应在组件中使用 useCallback 定义。

function Component(props){
  const onClick=useCallback(()=>{
     // Do some things with props or state
  },[])

  return <Something {...{onClick}} />
}

另一方面,如果您不想在函数中使用 props 或 state,请在组件外部定义它。

const computeSomethings=()=>{
   // Do some things with params or side effects
}

function Component(props){
  return <Something onClick={computeSomethings} />
}

对于 HTML 标签,您不需要 useCallback 因为它将在react端处理并且不会分配给 HTML

function Component(props){
  const onClick=()=>{
     // Do some things with props or state
  }

  return <Something {...{onClick}} />
}

编辑:钩子中的函数

对于钩子中的 use 函数例如 useEffect,我的建议是在 useEffect 中定义函数,如果你担心 DRY,让你的函数在钩子中纯粹调用它并给它你的参数。钩子 deps 呢?您应该/可以将所有参数添加到 hooks deps,但 useEffect 只需要 deps 应该会影响它们的更改。

你能举一个在函数组件内部使用钩子的例子吗?(未设置状态方法)
2021-05-15 00:04:33

我们可以useCallback在功能组件中使用如下所示的 React 钩子

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功能组件中列出函数的依赖关系
2021-04-18 00:04:33
UseCallback 仅用于记忆函数,以便在每次渲染时可以记忆函数本身,这需要依赖道具或状态。
2021-04-25 00:04:33
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 年的问题,并且已经回答了这么多票的问题。你可能不会得到你的投票。
2021-05-03 00:04:33