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

Posted

技术标签:

【中文标题】我应该在哪里声明在 useEffect() 钩子中调用的函数?【英文标题】:Where should I declare functions that are called inside a useEffect() hook? 【发布时间】:2019-11-12 23:13:27 【问题描述】:

所以我在使用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])

问题:

在这种情况下,我应该在 useEffect() 内声明 someFunction 还是将其放在外面(但在组件的主体内)是否安全?

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

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

是否有基本规则说明何时应在 useEffect 挂钩中声明函数,或者何时必须将其添加到依赖项数组中?

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

注意:

此代码在 CodeSandbox 上触发以下 eslint 警告。虽然它工作得很好。

React Hook React.useEffect 缺少一个依赖项:'someFunction'。要么包含它,要么删除依赖数组。 (react-hooks/exhaustive-deps)eslint

真实案例:

这是一个简化的例子。在我的真实案例中,这是一个带有过滤器组件的产品搜索页面。所以当我点击一个过滤器来激活它时(比如说,price <= 50),我触发了一个useEffect(),它正在“监听”activePriceFilters 状态变量。然后,该效果调用一个函数(在示例中为 someFunction),该函数将计算 filteredList 并将新的 productList 状态设置为新的 filteredList

片段

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"/>

【问题讨论】:

您可以阅读this FAQ answer。另外,this is an awesome article 关于useEffect 【参考方案1】:

在 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])

【讨论】:

这个例子是我真实案例场景的过度简化版本。 你能帮助我的真实案例吗?这是一个带有过滤器组件的产品搜索页面。所以当我点击一个过滤器来激活它时(比如说,价格 正如我所说,如果该函数将在 useEffect 之外使用,则需要在 useEffect 之外定义一个单独的函数,否则在 useEffect 中定义它并将其传递给状态更新 谢谢!但是从我的整体代码模式来看,您认为这是一个好方法吗?使用 useEffect 来“监听”状态过滤器变量的变化并调用一个为 filteredList 设置新状态的函数?抱歉打扰你。你的回答真的很有帮助。【参考方案2】:

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

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

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

【讨论】:

你能帮助我的真实案例吗?这是一个带有过滤器组件的产品搜索页面。所以当我点击一个过滤器来激活它时(比如说,price &lt;= 50),我触发了一个useEffect(),它正在“监听”activePriceFilters 状态变量。然后该效果调用一个函数(在示例中为someFunction),该函数将计算filteredList 并将新的productList 状态设置为新的filteredList。这种方法有什么问题吗?你会怎么处理这个?谢谢! 是否同步计算?还是从服务器检索它们? 它是同步的。我还应该补充一点,我有超过 1 个组件过滤器,以及超过一个具有不同有源过滤器的状态变量。如果其中任何一个发生变化,我应该计算一个新的 filteredList 状态,它作为道具传递给我的 ResultList 组件。 那我就做const [filters, setFilters] = useState([]); const result = data.filter(filters /*...*/); 是的,在这种情况下。

以上是关于我应该在哪里声明在 useEffect() 钩子中调用的函数?的主要内容,如果未能解决你的问题,请参考以下文章

如何使用/不使用 useEffect 钩子在 React JS 函数中获取更改的(新)状态值?

使用钩子获取数据时的意外行为

如何在 React 函数组件中不使用 useEffect 钩子获取数据?

如何在 UseEffect 中使用自定义方法?

如何在useEffect钩子反应中停止内存泄漏

React - nextjs 在 useEffect 中调用自定义钩子