用于获取数据的反应挂钩的序列化

时间:2019-08-20 21:56:59

标签: reactjs asynchronous react-hooks

我刚刚说过尝试使用React钩子,但是我还没有找到解决以下问题的方法。假设我有两个用于获取数据的自定义钩子。为简单起见,假设第一个不带参数,第二个不带参数,如下所示:

function sleep(ms) {
  return new Promise(resolve => setTimeout(resolve, ms));
}

// fetching takes 3 secs
function useFetch1() {

  const [text, setText] = useState("")

  useEffect(() => {
    const test = async () => {
      await sleep(3000)
      setText("text1")
    }
    test()
  }, [])

  return text
}

// fetching takes 1 sec
function useFetch2(input) {

  const [text, setText] = useState("")

  useEffect(() => {
    const test = async () => {
      await sleep(1000)
      setText(input + "text2")
    }
    test()
  }, [input])

  return text
}

这使我可以独立使用每个挂钩。到目前为止,一切都很好。但是,当第二次获取的参数取决于第一次获取的输出时,会发生什么。特别是,假设我的App组件是这样的:

function App() {

  const text1 = useFetch1()
  const text2 = useFetch2(text1) // input is dependent on first fetch

  // renders text2 and then text1text2
  return (
    <div>
      {text2} 
    </div>
  )
}

在这种情况下,渲染器将首先显示“ text2”,然后(2秒后),将显示“ text1text2”(即首次提取完成时)。有没有一种方法可以确保仅在useFetch2完成后才调用useFetch1

1 个答案:

答案 0 :(得分:1)

您可以实现所需的行为,但是: 设计上,必须在每个渲染器上调用挂钩函数-不允许有条件地调用挂钩,React无法处理它。

您可以在钩子内实现逻辑,而不是有条件地调用钩子,例如,useFetch1可以返回请求的状态,该状态可以在另一个钩子中用于有条件地基于该请求执行某些操作。

示例:

function useFetch1() {

  const [text, setText] = useState("")

  // a bool to represent whether the fetch completed
  const isFinished = useState(false)

  useEffect(() => {
    const test = async () => {
      await sleep(3000)
      setText("text1")
    }
    test()
  }, [])

  return {text, isFinished} // Return the isFinished boolean
}

function useFetch2(input, isFetch1Finished) {

  const [text, setText] = useState("")

  useEffect(() => {
    if (!isFetch1Finished) {
      return; // fetch1 request not finished, don't fetch
    }
    const test = async () => {
      await sleep(1000)
      setText(input + "text2")
    }
    test()
  }, [input])

  return text
}

function App() {

  const {text: text1, isFinished} = useFetch1()
  const text2 = useFetch2(text1, isFinished) // input is dependent on first fetch, but is aware whether the first fetch is finished or not.

  return (
    <div>
      {text2} 
    </div>
  )
}

现在,您可以在第二次访存中使用isFinished中的useFetch1布尔标志来确定是否执行某项操作。

更好的方法
这完全取决于您的用例,但是您可能不希望将2个钩子的逻辑耦合在一起。我建议最好将useFetch2放在一个单独的组件中,该组件根据状态useFetch1有条件地呈现。

return (
  {isFetch1Finished && <SecondComponent text={text1}>}
)

现在,通过在第一个请求完成后有条件地呈现第二个组件,简化了钩子逻辑。与第一种方法相比,这是一个更好的设计。

您是否还需要2个钩子? 问问自己是否甚至需要两个钩子,使用promises在单个钩子中实现同步行为将是微不足道的。

示例:

useFetch(() => {
  const [state, setState] = useState("");

  useEffect(() => {
    fetch('http://..').then(res1 => {
      fetch('http://..').then(res2 => {
        // In this scope you have both `res1` and `res2`.
        setState(res1 + res2);
      })
    })
  }, [])

  return state;
})