【问题标题】:setState with value of previously returned custom hooksetState 具有先前返回的自定义挂钩的值
【发布时间】:2019-02-22 19:29:14
【问题描述】:

我在这里找不到类似的问题,所以这里是: 我创建了一个自定义钩子useBudget 来获取一些数据。

const initalState = {
    budget_amount: 0,
};

const useBudget = (resource: string, type: string) => {
    const [budgetInfo, setBudget] = useState(initalState);
    useEffect(
      () => {
          (async (resource, type) => {
              const response = await fetchBudgetInfo(resource, type);
              setBudget(response);
          })(resource, type);
    }, []);

    return [budgetInfo];
};

在使用该钩子的组件上,我有这样的东西:

  const [budgetInfo] = useBudget(resource, type);
  const [budgetForm, setBudgetForm] = useState({ warningMsg: null, errorMsg: null, budget: budgetInfo.budget_amount });

问题是:这个组件的初始状态在抓取后没有更新。 budget 最初以 0 呈现并保持这种方式。如果console.log(budgetInfo) 紧随其后,budget 会更新,但状态不会。

我相信这是由于异步性而发生的,对吧?但是如何解决这个问题?

谢谢!

【问题讨论】:

  • 你可以在例如创建一个Minimal, Complete, and Verifiable example吗? CodeSandbox?很难说当前问题中的代码有什么问题。
  • 我稍后会尝试这样做并将其发布在这里,但没有更多内容。我只是想根据获取数据的自定义钩子的返回来获得一个本地状态。
  • 钩子没问题。它不起作用意味着它使用不正确。没有证据表明 fetchBudgetInfo 返回正确的对象。
  • @estus,它确实返回了正确的对象。 budget 更新后的值不同于 0(初始值)
  • budget_amount 而不是budget,不是吗?

标签: reactjs react-hooks


【解决方案1】:

我可以解决,但是,我不是 100% 认为这是最好/正确的方法。据我所知,由于异步性,我仍在读取旧状态值,解决此问题的方法是将状态设置在 useEffect 内。我会:

  const [budgetInfo] = useBudget(resource, type);
  const [appState, setAppState] = useState({ budget: budgetInfo.budget_amount });

  useEffect(
    () => {
      setAppState({ budget: budgetInfo.budget_amount });
  }, [budgetInfo]);

但它现在可以工作了!

工作示例:https://stackblitz.com/edit/react-wiewan?file=index.js

【讨论】:

    【解决方案2】:

    使用useEffect 安排的效果不会阻止浏览器更新屏幕 - 这就是屏幕上显示0 (initialState) 的原因。获取值后,组件保持不变,因为它自己的状态没有变化(budgetForm)。

    一旦获取budgetInfo,您的解决方案就会更新组件的状态,从而触发重新渲染,这可行,但似乎是一种解决方法。

    useBudget 可以单独使用:

    const useBudget = (resource, type) => {
      const [budgetInfo, setBudget] = useState(initalState);
    
      const fetchBudgetInfo = async () => {
        const response = await (new Promise((resolve) => resolve({ budget_amount: 333 })))
        setBudget(response)
      }
    
      useEffect(() => {
        fetchBudgetInfo(resource, type)
      }, [])
    
      return budgetInfo
    };
    
    const App = props => {
      const { budget_amount } = useBudget('ok', 'ok')
    
      return (
        <h1>{budget_amount}</h1>
      );
    }
    

    fetchBudgetInfo 被拆分出来,因为如果我们将效果函数设为异步 (useEffect(async () =&gt; { ... })),它将隐式返回一个承诺,这违背了它的设计——唯一的返回值必须是一个将用于打扫干净。 Docs ref

    或者,考虑在没有自定义挂钩的情况下检索数据:

    const fetchBudgetInfo = async (resource, type) => {
      const response = await fetch(resource, type)
      return response
    }
    
    useEffect(() => {
      const response = fetchBudgetInfo(resource, type)
      setAppState((prevState) => { ...prevState, budget: response.budget_amount });
    }, []);
    

    值得注意的是,我们正在手动将旧状态与新值合并,如果 appState 包含多个值,这是必要的 - 这是因为 useState 不会像 this.setState 那样浅层合并对象,而是完全替换状态多变的。 Doc ref

    在一些相关的说明中,使用对象来保持状态并没有错,但是using multiple state variables 提供的优势很少 - 更精确的命名和进行单独更新的能力。

    【讨论】:

    • 感谢贡献者。但我不知道是否同意我的解决方案是一种解决方法。通过使用自定义钩子,我不知道是否还有其他解决方案。但是,是的,您的方法将是另一种获取数据的方法。
    猜你喜欢
    • 2022-01-27
    • 1970-01-01
    • 2021-11-22
    • 1970-01-01
    • 2020-01-23
    • 1970-01-01
    • 1970-01-01
    • 2020-08-04
    • 2021-11-16
    相关资源
    最近更新 更多