在useEffect()钩子内调用的函数应该声明在哪里?

11

所以,在使用一个调用依赖于状态的函数的useEffect时,我遇到了以下情况。

例子:

// INSIDE APP COMPONENT

const [someState, setSomeState] = React.useState(0);
const [someTrigger, setSomeTrigger] = React.useState(false);

function someFunction() {
  return someState + 1;  
}

React.useEffect(() => {

  const newState = someFunction();  // 'someFunction' IS BEING CALLED HERE
  setSomeState(newState);

},[someTrigger])

问题:

在这种情况下,我应该将someFunction声明在useEffect()中还是安全保险起见将其放在组件体内但是useEffect()外部?

我可以将其添加到dependency数组中,但这会损害我的代码可读性,因为我想专注于trigger

useEffect()将在新的渲染后运行,我是否可以安全地假设它将具有我在其中调用的函数的最新副本?

是否有一个基本规则,告诉我们何时应在useEffect钩子中声明函数,或者什么情况下必须将其添加到依赖项数组中?

编辑: 请注意,useEffect需要拥有这些函数的最新副本,因为这些函数需要访问一些最新的、最新的state变量。

注意:

该代码在CodeSandbox上触发以下eslint警告。尽管它完美无缺。

React Hook React.useEffect has a missing dependency: 'someFunction'. Either include it or remove the dependency array. (react-hooks/exhaustive-deps)eslint

真实案例场景:

这只是一个简单的示例。在我的真实情况中,这是一个带有筛选器组件的产品搜索页面。因此,当我单击一个过滤器以激活它时(例如,price <= 50),我会触发"监听"activePriceFilters状态变量的useEffect()。然后,该效果调用一个函数(这里是someFunction),该函数将计算筛选列表,并使用新的筛选列表设置新的productList状态。

片段:

function App() {
  
  const [someState, setSomeState] = React.useState(0);
  const [someTrigger, setSomeTrigger] = React.useState(false);
  
  function someFunction() {
    return someState + 1;  
  }
  
  React.useEffect(() => {
  
    const newState = someFunction();
    setSomeState(newState);
  
  },[someTrigger])
  
  return(
    <React.Fragment>
      <div>I am App</div>
      <div>My state: {someState}</div>
      <button onClick={()=>setSomeTrigger((prevState) => !prevState)}>Click</button>
    </React.Fragment>
  );
}

ReactDOM.render(<App/>, document.getElementById('root'));
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.8.3/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.8.3/umd/react-dom.production.min.js"></script>
<div id="root"/>


1
你可以阅读这个FAQ答案。此外,关于useEffect这篇文章非常棒 - devserkan
2个回答

10

决定在useEffect内部还是外部定义函数取决于所有函数的调用位置。

如果您的函数仅从useEffect中调用,则将其定义在useEffect内部是有意义的。

但是,如果同一个函数既在useEffect中被调用,又在其他事件处理程序或其他效果中被调用,则需要在useEffect之外定义该函数。

附注:在您的情况下,您只需更新状态,无需定义单独的函数。

function App() {
  
  const [someState, setSomeState] = React.useState(0);
  const [someTrigger, setSomeTrigger] = React.useState(false);
  

  React.useEffect(() => {
    setSomeState(oldState => oldState + 1);
  
  },[someTrigger])
  
  return(
    <React.Fragment>
      <div>I am App</div>
      <div>My state: {someState}</div>
      <button onClick={()=>setSomeTrigger((prevState) => !prevState)}>Click</button>
    </React.Fragment>
  );
}

ReactDOM.render(<App/>, document.getElementById('root'));
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.8.3/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.8.3/umd/react-dom.production.min.js"></script>
<div id="root"/>

就您的情景而言,您应该这样编写:

关于您的情景,您需要像这样编写它:

useEffect(() => {
    const calcVal = (oldState) => {
         // calculate previous state based on oldState
    }

    setSomeState(calcVal);
}, [activePriceFilters])

这个例子是我真实情况的一个过于简化的版本。 - cbdeveloper
你能帮助我处理一下真实案例吗?这是一个带有筛选组件的产品搜索页面。因此,当我点击筛选器以激活它(比如,价格<=50),我会触发一个useEffect(),它正在“监听”activePriceFilters状态变量。然后,该效果调用一个函数(在示例中为someFunction),该函数将计算filteredList,并使用新的filteredList设置新的productList状态。这种方法有什么问题吗?您会如何处理?谢谢! - cbdeveloper
1
就像我之前所说的,如果这个函数需要在 useEffect 之外使用,你需要在 useEffect 之外定义一个单独的函数。否则,你可以在 useEffect 中定义它,并将其传递给状态更新。 - Shubham Khatri
谢谢!但是从我的整体代码模式来看,你认为这是一个好的方法吗?使用 useEffect 来“监听”状态过滤器变量的变化,并调用一个函数来设置 filteredList 的新状态?抱歉打扰你了。你的回答真的很有帮助。 - cbdeveloper
在useEffect内部声明函数是否意味着每次触发useEffect时都会重新创建它们?还是Javascript运行时足够聪明,可以缓存它们或其他什么? - mpelzsherman

1

设置另一个状态根本不应该是一个效果。

 const [someState, setSomeState] = React.useState(0);

 function incrementState() {
    setSomeState(someState => someState + 1);
 }

你能帮我处理一下真实场景吗?这是一个带有筛选组件的产品搜索页面。因此,当我点击筛选器以激活它(比如说,价格<=50),我会触发一个“监听”activePriceFilters状态变量的useEffect()。然后该效果调用一个函数(在示例中为someFunction),该函数将计算filteredList并使用新的filteredList设置新的productList状态。这种方法有什么问题吗?你会如何处理?谢谢! - cbdeveloper
它是同步计算还是从服务器检索? - Jonas Wilms
它是同步的。我还应该补充说明,我有多个组件过滤器,以及多个具有不同活动过滤器的状态变量。如果它们中的任何一个发生变化,我应该计算一个新的 filteredList 状态,并将其作为 props 传递给我的 ResultList 组件。 - cbdeveloper
1
然后我只需要这样做 const [filters, setFilters] = useState([]); const result = data.filter(filters /*...*/); - Jonas Wilms
如果是异步的呢?那么你会在 useEffect 中执行吗?像这样:useEffect(()=>{ // 激活加载状态 // 等待异步调用 // 取消加载状态 // },[activeFilter] ); 类似这样的操作? - cbdeveloper
1
是的,在那种情况下。 - Jonas Wilms

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接