【问题标题】:Trigger useEffect from functional Component with global variable使用全局变量从功能组件触发 useEffect
【发布时间】:2021-10-30 03:52:18
【问题描述】:

我的问题是是否可以使用组件外部的变量来触发 useEffect。

在我的情况下,我有这个主要组件,它的 useEffect 负责在每次变量“刷新”更改时更新信息。

function Main() {

const [data, setData] = useState([])
const [refresh, setRefresh] = useState(false)

useEffect(async () => {
    await axios.get(`${process.env.REACT_APP_SERVER_URL}/api/data/`)
        .then(res => {
            setData(res.data); 
        })
}, [refresh]);

然后我有一个函数,我可以在触发 useEffect 的组件或子组件内部调用,更新数据。

    const refreshData = () => setRefresh(!refresh);

到目前为止一切顺利,它可以按我的意愿工作,但现在我需要导出此函数以在与此无关的组件中使用,但我知道您无法导出在组件内声明的函数。

所以我的想法是在组件外部创建同样的函数,如下所示:

    let refreshOutside = false;

    export const refreshMainFromOutside = () => {
        refreshOutside = !refreshOutside;
    }

所以现在我可以将变量“refreshOutside”添加到 useEffect,如下所示:

useEffect(async () => {
    await axios.get(`${process.env.REACT_APP_SERVER_URL}/api/data/`)
        .then(res => {
            setData(res.data); 
        })
}, [refresh, refreshOutside]);

但是如果我将它导入其他组件并调用它不会触发 useEffect 的方法,我对反应有点陌生,但我认为这是因为组件没有重新渲染。

有什么解决方案可能适用于我的情况吗?

提前致谢。

【问题讨论】:

  • 为什么不简单地定义一个单独的函数来获取数据并在 useEffect() 中使用它并作为回调传递给应该触发刷新的组件?
  • 这样做有什么意义?你可以只调用一个调用你的 api 的函数
  • 我可以,但是我无法更新主组件内的状态

标签: javascript reactjs use-effect


【解决方案1】:

我建议你将你的钩子放在另一个文件中,例如 useComponent.js 并将你的 refreshData 导出为其中的 const,然后在你希望的任何组件中使用该钩子:

const useComponent = () => {
    const [data, setData] = useState([])
    const [refresh, setRefresh] = useState(false)

    useEffect(async () => {
        await axios.get(`${process.env.REACT_APP_SERVER_URL}/api/data/`)
            .then(res => {
                setData(res.data); 
            })
    }, [refresh, refreshOutside]);

    const refreshData = () => setRefresh(!refresh);

    export { refreshData }
}

export default useComponent

在任何组件中导入钩子,然后解构函数并使用它们:

import useComponent from '../hooks/useComponent'

const MyComponent = () => {

   const { refreshData } = useComponent()

   return <button onClick={refreshData}>Click to refresh!</button>

}

export default MyComponent

【讨论】:

  • React 不允许我在组件内导出 refreshData,“'import' 和 'export' 可能只出现在顶层”
  • useComponent 不是一个组件,它是一个自定义钩子。它允许您重用这些功能。如果你想拥有一个全局变量,你可以在这个钩子中使用上下文钩子。
【解决方案2】:

正如评论中提到的,您可以简单地定义一个单独的函数来获取数据(并使用 useCallback() 钩子记住它),然后您可以在主组件和任何子组件中的任何位置使用该函数您将其作为道具传递给谁。

也许举个例子会更容易理解:

const Main = () => {
  const [data, setData] = React.useState([]);
  
  const updateData = React.useCallback((startIndex) => {
    /*
    axios.get(`${process.env.REACT_APP_SERVER_URL}/api/data/`)
      .then(result => {
          setData(result.data); 
      })
    */
    axios.get('https://jsonplaceholder.typicode.com/posts/')
      .then((result) => {
        console.log('fetching data...');
        setData(result.data.slice(startIndex, startIndex + 5));
        console.log('... data updated');
      });
  }, [setData]);
  
  React.useEffect(() => {
    updateData(0); // fetch & update `data` when the Main component mounts
  }, [])
  
  return (
    <div>
      <h1>Main</h1>
      {
        data.length > 0
          ? <ul>{data.map(item => <li>{item.title}</li>)}</ul>
          : <p>'There are no data'</p>
      }
      <button
        onClick={() => updateData(5)} // fetch & update `data` on request, on button click
      >Refresh from Main</button>
      <Child handleClick={updateData} />
    </div>
  )
}

const Child = ({handleClick}) => {
  return (
    <React.Fragment>
      <h1>Child</h1>
      <button
        onClick={() => handleClick(10)} // fetch & update `data` on request, on button click
      >Refresh from Child</button>
    </React.Fragment>
  )
}

ReactDOM.render(<Main />, document.getElementById('app'));
<script src="https://cdnjs.cloudflare.com/ajax/libs/axios/0.21.1/axios.js"></script>
<script crossorigin src="https://unpkg.com/react@17/umd/react.production.min.js"></script>
<script crossorigin src="https://unpkg.com/react-dom@17/umd/react-dom.production.min.js"></script>

<div id="app"></div>

附:在上面的示例中,我为updateData() 函数使用了一个参数(startIndex),只是为了保持data 状态简短,并在单击不同按钮时使其更改(因为我使用的API 总是返回相同的数据)。在实际使用情况下,您不太可能做这样的事情,因为您可以在 API 端实现分页(如果需要),并且您的 API 响应应该随着时间而改变(或者您不需要刷新数据的方法)。 无论如何,关键是我在updateData() 函数体内所做的大部分是无关紧要的; sn-p 的主要内容应该是如何处理需要在钩子、主组件和子组件内部调用的函数。

【讨论】:

  • 嗨,谢谢你的回答,我想我明白你在那里做了什么,但就我而言,也许不是最佳实践,是我想要访问刷新的组件是与主要组件无关(由于项目的各种原因)。然而,在您的解决方案中,您需要与主要组件建立关系吗?我说的对吗?
  • 是的,在我提供的示例中,Child 必须是Main 的后代。如果data是唯一需要被无关组件访问的状态“片断”,也许你可以尝试使用依赖useContext()处理问题(你可能想看看这篇文章: robinwieruch.de/react-usecontext-hook)。如果还有其他“部分”状态需要在不相关的组件之间共享,那么您可能需要考虑使用像 Redux 或类似的状态容器。
  • 感谢您的回答,我会看看这些。实际上,我唯一想分享的是改变刷新值的功能。常量 refreshData = () => setRefresh(!refresh);由于它在组件内声明,我不能只导出它。我必须想办法做到这一点,但无论如何谢谢
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2018-04-26
  • 2011-05-07
  • 1970-01-01
相关资源
最近更新 更多