Javascript 反应钩子:从useEffect分派操作

Javascript 反应钩子:从useEffect分派操作,javascript,reactjs,redux,redux-saga,Javascript,Reactjs,Redux,Redux Saga,我的文件夹结构: |--App |--Components |--PageA.js |--PageB.js |--PageC.js |--common-effects |--useFetching.js 我正在重构代码,使用react从API获取数据。 我想从useffetching.js中的useffect发送一个操作,该操作被saga中间件截获。仅当组件(PageA、PageB、PageC)装入时,才应分派操作 我正在使用redux、react re

我的文件夹结构:

|--App
  |--Components
    |--PageA.js
    |--PageB.js
    |--PageC.js
  |--common-effects
    |--useFetching.js
我正在重构代码,使用react从API获取数据。 我想从useffetching.js中的useffect发送一个操作,该操作被saga中间件截获。仅当组件(PageA、PageB、PageC)装入时,才应分派操作

我正在使用reduxreact reduxredux saga

PageA.js:

PageB和PageC组件的类似代码

我已经抽象了可重用代码,以便在
useFetching
customhook中获取数据

useFetching.js


我不知道如何在useFetching中访问redux
dispatch
。我尝试了
useReducer
效果,但是传奇没有成功。

您需要将绑定动作创建者或对
dispatch
的引用传递给钩子。这些将来自连接的组件,与您通常使用React-Redux相同:

功能MyComponent(道具){
使用抓取(道具。抓取某物);
回来做一些抓取!
}
常量映射分派={
吸引
};
导出默认连接(null,mapDispatch)(MyComponent);
然后,钩子应该在effect中调用绑定的动作创建者,后者将相应地分派动作

另外,请注意,当前挂钩将在每次重新渲染组件时重新运行效果,而不仅仅是第一次。您需要修改钩子,如下所示:

const useFetching=someFetchActionCreator=>{
useffect(()=>{
someFetchActionCreator();
}, [])
}
使用react-redux挂钩的版本: 您甚至可以使用react redux的
useDispatch
完全切断连接功能:

export default function MyComponent() {
  useFetching(fetchSomething);

  return <div>Doing some fetching!</div>
}
编辑:根据@yonga springfield的建议,从自定义钩子中删除了调度


注意:React保证分派函数标识是稳定的,并且在重新渲染时不会更改。这就是为什么从useEffect或useCallback依赖项列表中省略是安全的。

这只是为了给@Alex Hans的答案带来一些优化

根据文件。自定义钩子是一个JavaScript函数,其名称以“use”开头,可以调用其他钩子

考虑到这一点,我们不需要将对dispatch函数的引用作为参数发送到useFetching钩子,而是不发送它,而是通过适当的导入从useFetching钩子中使用它

这是我的意思的摘录

import { useDispatch } from 'react-redux';

const useFetching = (someFetchActionCreator) => {
    const dispatch = useDispatch()

    useEffect(() => {
    dispatch(someFetchActionCreator());
  }, [])
}
我不能确定这个例子是否适合你的案例,但我只是试图解释本文背后的想法/概念


希望这对未来的任何参与者都有帮助。

Alex Hans在分派方面的决策是正确的,但为了消除api的请求循环,您可以指定对分派的依赖性(我使用过)

useEffect(() => {
   fetchData();
 }, []);

 async function fetchData() {
   try {
     await Auth.currentSession();
     userHasAuthenticated(true);
   } catch (e) {
     if (e !== "No current user") {
       alert(e);
     }
   }
   dispatch(authentication({ type: "SET_AUTHING", payload: false }));
 }
import React,{useffect}来自“React”
从“react redux”导入{useDispatch}
从“axios”导入axios
从“../features/itemsSlice”导入{getItemsStart、GetItemsAccess、getItemsFailure}
const fetchItems=()=>异步调度=>{
试一试{
调度(getItemsStart());
const{data}=wait axios.get('url/api')
调度(GetItemsAccess(数据))
}捕获(错误){
调度(getItemsFailure(错误))
}
}
常数PageA=()=>{
const dispatch=usedpatch()
const{items}=useSelector(state=>state.dataSlice)
useffect(()=>{
分派(fetchItems())
}[快讯])
返回(
    {items.map(item=>
  • {item.name}
  • }
) } 导出默认页面A

在useffect(()=>{…},[dispatch])中传递dispatch的依赖参数是很重要的。

我不认为钩子应该用于像那样的arrow函数中。另外,如果你想使用redux,他们已经为此设置了钩子:通过arrow函数,你是说,
constuseffetching=actionArgs=>{}
,如果我没有错的话?是的,但看起来我错了。我的所有组件都使用一个动作
export const fetchData=payload=>({type:'FETCH_DATA'})
sagas
然后将通过分派相应的操作将值存储在页面的相应减缩器中。根据您的解决方案,我必须从每个组件传递分派的引用。您要分派的任何操作,如刚才在该注释中编写的
fetchData
函数。我更改了代码相应地,但仍然是saga
yield take('FETCH_DATA'))但是,我仍然觉得通过引用从每个组件到自定义钩子的传递比我在HOCH中的重复有点重复。考虑到我不想将动作创建器特别连接到我的钩子上。有什么想法吗?应该<代码>调度
是否包含在useEffect的依赖项数组中?在useEffect中放置dispatch时,我遇到一个错误:
React-Hook useEffect缺少依赖项:“dispatch”。请包含它或删除依赖项数组React-hooks/deps
@pianoman102-Technicaly,是的。即使注释中提到它的id实体是稳定的,不会改变;这并不意味着yo-ushoudl会忽略它,如果使用CRA,lint规则将继续抛出。这里描述了更多上下文:“React保证分派函数标识是稳定的”源代码?我找不到任何地方。“马丁。谢谢,但是我看到的是,<代码>调度<代码> > <代码>用户> <代码>是稳定的,但是关于<代码>调度< >代码>从<代码>使用调度> /代码>从<代码> Read ReDux < /代码>?欢迎添加关于您如何发布代码工作的原因的解释。在useffect(()=>{…},[dispatch])中传递dispatch的依赖参数非常重要,太棒了!谢谢!
import { useDispatch } from 'react-redux';

const useFetching = (someFetchActionCreator) => {
  const dispatch = useDispatch();
  useEffect(() => {
    dispatch(someFetchActionCreator());
  }, [])
}
import { useDispatch } from 'react-redux';

const useFetching = (someFetchActionCreator) => {
    const dispatch = useDispatch()

    useEffect(() => {
    dispatch(someFetchActionCreator());
  }, [])
}
useEffect(() => {
   fetchData();
 }, []);

 async function fetchData() {
   try {
     await Auth.currentSession();
     userHasAuthenticated(true);
   } catch (e) {
     if (e !== "No current user") {
       alert(e);
     }
   }
   dispatch(authentication({ type: "SET_AUTHING", payload: false }));
 }
  import React, { useEffect } from 'react'
  import { useDispatch } from 'react-redux'
  import axios from 'axios'
  import { getItemsStart, getItemsSuccess, getItemsFailure } from '../features/itemsSlice'

  const fetchItems = () => async dispatch => {
    try {
      dispatch(getItemsStart());
      const { data } = await axios.get('url/api')
      dispatch(getItemsSuccess(data))
    } catch (error) {
      dispatch(getItemsFailure(error))
    }
  }

  const PageA = () => {
    const dispatch = useDispatch()
    const { items } = useSelector(state => state.dataSlice)
   
    useEffect(() => {
       dispatch(fetchItems())
    }, [dispatch])

    return (
      <ul>
         {items.map(item => <li>{item.name}</li>}
      </ul> 
    )
  }
  
  export default PageA