类组件引用并设置自己的静态变量 - 什么是功能组件的等价物?

Class components referencing and setting their own static variables - what is the functional component equivalent?

提问人:user573 提问时间:10/16/2023 更新时间:10/16/2023 访问量:42

问:

使用类组件时,我能够设置静态变量,这些变量将持续存在于类的每个后续实例上。这允许我在组件上设置一种状态,即使多次调用同一组件也是如此。firstRun

我试图在功能组件中创建这种行为,但是当组件被多次使用时,它似乎每次都会忘记它的状态、引用等。

在下面的代码中,我有两个组件,一个函数和一个类。这些组件中的每一个在 中被调用三次。在类组件中,我能够直接设置它,然后我能够在同一组件的进一步包含中引用它。在功能组件中,我尝试了相同的方法,但这似乎仅适用于组件的每个实例,并且在每个新组件上都会被遗忘。AppClassComponent.firstRunuseRef

const { useEffect, useRef } = React;

const FunctionalComponent = (props) => {
  const { id } = props;
  const mounted = useRef(false);
  useEffect(() => {
    if (!mounted.current) {
      console.log('Initial functional component load...', id);
      mounted.current = true;
    } else {
      console.log('Functional component has already been initialised.', id)
    }
  }, []); 
  return (
    <div>Hello, functional component!</div>
  );
};

class ClassComponent extends React.Component {
  constructor(props) {
    super(props);
    this.createSomething();
  }
  
  static firstRun = true;
  
  createSomething() {
    if (ClassComponent.firstRun) {
      console.log('Initial class component load...', this.props.id);
      ClassComponent.firstRun = false;
    } else {
      console.log('Class component has already been initialised.', this.props.id);
    }
  }

  render() {
    return (
      <div>Hello, class component!</div>
    );
  }
}

function App() {
  return (
    <div>
      <ClassComponent id="class-component-1" />
      <ClassComponent id="class-component-2" />
      <ClassComponent id="class-component-3" />
      <FunctionalComponent id="functional-component-1" />
      <FunctionalComponent id="functional-component-2" />
      <FunctionalComponent id="functional-component-3" />
    </div>
  )
}

ReactDOM.render(<App />, document.querySelector("#app"));
<div id="app"></div>

<script src="https://cdnjs.cloudflare.com/ajax/libs/react/18.2.0/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/18.2.0/umd/react-dom.production.min.js"></script>

类组件运行良好 - 我得到一个初始加载日志,然后两个日志说它已经初始化 - 太好了!但是,功能组件记录了三个初始加载消息。

我尝试过使用 ,但据我了解,这仅适用于重新渲染,而不适用于组件的单独出现。使用时,这似乎也是同样的情况。useStateuseRef

我已经阅读了有关函数闭包的信息,并尝试使用下面的代码粗略地实现一个闭包,但这又只是给了我三个日志:Initialised...

const checkInitial = () => {
  let initial = true; 
  return {
    get: function() { return initial },  
    set: function(state) { initial = state; }
  };
}
...
const FunctionalComponent = (props) => {
  const { id } = props;
  const mounted = useRef(false);
  useEffect(() => {
    const firstRun = checkInitial();
    if (firstRun.get()) {
      console.log('Initial...', id);
      firstRun.set(true);
    } else {
      console.log('Already run...', id);
    }
  }, []); 
  return (
    <div>Hello, functional component!</div>
  );
};

我相信设置上下文变量可能能够解决这个问题,但我宁愿现在不使用它。我也知道我可以将状态提升到父级,但我想避免这种情况,因为它很可能会导致重新渲染。

这种情况似乎很容易用类组件解决,但这些组件现在已经过时了。有没有一种简单的方法可以纯粹使用功能/组件来做到这一点?

干杯!

javascript reactjs react-hooks react-functional-component

评论

1赞 Henry Woody 10/16/2023
您可以按照建议“将状态提升到父级”,但使用 instead 以避免重新渲染。useRefuseState
0赞 user573 10/16/2023
很高兴知道,谢谢!

答:

2赞 Bergi 10/16/2023 #1

任何钩子,无论是 还是 ,都是组件的每个实例。useStateuseRef

如果你真的想要一个静态变量1,只需执行与组件完全相同的操作 - 将其存储在对象本身上:classfunction

function FunctionComponent({ id }) {
  if (FunctionComponent.firstRun) {
    console.log('Initial function component render...', id);
    FunctionComponent.firstRun = false;
  } else {
    console.log('Function component has already been rendered before.', id);
  }
  return (
    <div>Hello, function component!</div>
  );
}
FunctionComponent.firstRun = true;

不过,更习惯的做法是只在定义函数组件的模块作用域中声明一个变量:

let firstRun = true;
function FunctionComponent({ id }) {
  if (firstRun) {
    console.log('Initial function component render...', id);
    firstRun = false;
  } else {
    console.log('Function component has already been rendered before.', id);
  }
  return (
    <div>Hello, function component!</div>
  );
}

如果不希望日志在每次呈现组件时都出现,而只显示一次,则在装载组件时,可以使用效果或状态的初始值设定项:

let firstRun = true;
function FunctionComponent({ id }) {
  useEffect(() => {
    if (firstRun) {
      console.log('Initial function component mount...', id);
      firstRun = false;
    } else {
      console.log('Function component has already been mounted elsewhere.', id);
    }
  }, []);
  return (
    <div>Hello, function component!</div>
  );
}
let firstRun = true;
function FunctionComponent({ id }) {
  const [isFirst] = useState(() => {
    if (firstRun) {
      firstRun = false;
      return true;
    } else {
      return false;
    }
  });
  return (
    <div>Hello, {isFirst && 'first'} function component!</div>
  );
}

1:你可能想要一个静态变量。对于常量来说,这是可以的,但是一旦你有了有状态的静态变量,它本质上就是全局状态。避免这种情况。

评论

0赞 user573 10/16/2023
感谢您的回答 - 这是很多有用的信息!我想我会在模块范围内声明它,因为这似乎是最合适的方法。在函数/全局状态上存储变量时,是否有任何特殊原因应该避免这种情况?
1赞 Bergi 10/16/2023
取决于你问谁它是否“可接受”:-)如果您希望任何引用该函数的人都能访问静态变量,并将其用作命名空间,这将非常有用。这是一种在不使用模块模式的情况下避免额外全局变量的流行方法。