在 React 函数组件外部使用变量是一种反模式吗?

Is it an anti-pattern to use variables the outside of React function component?

下面的 case 是 React 中的反模式吗?

出于某些原因,变量和函数可能会在 React 函数组件之外声明。例如,计时器 ID 和其他可以使用如下:

import React from "react";

const myFunc = () => { // no purpose
    /*some codes*/
}
let myBool = false; // no purpose
let timerId = 0;
    
const MyComponent = () => {
    const [name, setName] = useState("");
    
    /* some codes */

    useEffect(() => {
        timerId = setTimeout(() => {
            /* some codes */
        }, 1000);
            
        return () => clearTimeout(timerId);
    }, []);
        
    return (
        <div>Hello world</div>
    );
}

export default MyComponent;

对于可能改变的变量,比如在你的例子中,是的,它很可能被认为是一个问题,因为你不能指望任何使用具有一致效果的组件,因为它不是纯粹的,而是取决于外部变量的当前状态(而不是 React 状态)。

例如,如果您有一个 MyComponent,后来您决定向您的站点添加另一个部分,该部分也使用了 MyComponent,并且同时呈现了这两个部分,那么您现有的代码将导致问题,因为两个组件将共享相同的 timerId 变量 - 第一个渲染的组件将丢失其 timerId。 (只有第二个要渲染的组件才能清除持久性外部 timerId

在这些情况下,组件内部使用的值属于该组件的给定实例,而不是整个应用程序,您应该使用状态(与 useState) 而不是外部变量。

一般来说,使用外部标识符并不总是一个坏主意,但恰恰相反,对于不会改变并且对抽象有用的常量值,例如 URL、API 键, 分离非 React 函数,等等。以下类型的模式并不少见:

import { makeApiCall } from './makeApiCall';

export const SomeComponent = () => {
  // ...more code here dealing with state
  const handleClick = () => {
    makeApiCall()
      .then(handleSuccess)
      .then(handleFail);
  };
  return (
    <button onClick={handleClick}>click</button>
  );
};

这样做不是犯罪。但在你这样做之前,想想你为什么要这样做。

如果你想像export { MyComponent, myFunc, timerId, myBool};一样将这些变量连同组件一起暴露给外界,那么使用这种模式是有意义的。否则最好在功能组件本身内部声明 variable/function。

是的,这是一个反模式。 IMO 功能组件背后的整个想法是使组件纯净。使 React 函数组件变得纯净,使其更易于预测,您的输出仅由函数的输入决定。在函数范围之外使用变量会使函数不再纯粹。使用纯函数有几个好处。我强烈推荐使用纯功能组件。

https://medium.com/javascript-scene/master-the-javascript-interview-what-is-a-pure-function-d1c076bec976

是的,它被认为是反模式,您不能只在组件内定义这些变量,因为值将在重新呈现组件时重新初始化。 我们在 React 中有 ref 对象来实现这一点:

import React from "react";

const myFunc = () => { // no purpose
    /*some codes*/
}
    
const MyComponent = () => {
    const [name, setName] = useState("");
    const myBool = useRef(false);
    const timerId = useRef(0);
    
    /* some codes */

    useEffect(() => {
        timerId.current = setTimeout(() => {
            /* some codes */
        }, 1000);
            
        return () => clearTimeout(timerId.current);
    }, []);
        
    return (
        <div>Hello world</div>
    );
}

export default MyComponent;

myBooltimerId 不会在重新渲染时改变,您可以根据需要改变这些对象。您可以阅读更多 here.