简体   繁体   English

如何在两个自定义钩子之间共享状态?

[英]How can I share state between two custom hooks?

I'm trying to fetch data from Spotify API. 我正在尝试从Spotify API获取数据。 Since I need an access token to do this, I built a custom hook to parse the token from the URL that comes from the server. 由于我需要访问令牌来执行此操作,因此我构建了一个自定义钩子,以从服务器的URL中解析令牌。
I also have another custom hook with the actual request to the API that takes the parsed token as an argument. 我还有另一个自定义钩子,具有对API的实际请求,该请求将已解析的令牌作为参数。 Both are gathered in a parent hook. 两者都聚集在父挂钩中。

I cannot make this work since the token is never reaching the scope of the request hook so it fails. 我无法完成这项工作,因为令牌从未达到请求挂钩的范围,因此失败了。 If I parse the token and make the request within the same hook everything works out just fine. 如果我解析令牌并在同一钩子内发出请求,则一切正常。 I intended to make a hook for every request since it's not just one, that's why I wanted to pass the token as an argument. 我打算为每个请求创建一个钩子,因为它不仅是一个请求,这就是为什么我想将令牌作为参数传递的原因。

Token custom hook 令牌自定义钩

export default () => {
  const [ token, setToken ] = useState('')
  useEffect(() => {
    const { access_token } = queryString.parse(window.location.search)
    return setToken(access_token)
  }, [])
  return token
}

Request hook 请求挂钩

export default function useFetchUserData(token) {
  //state
  const initialUserState = {
    display_name: '',
    external_url: '',
    images: ''
  }
  const [ userData, setUserData ] = useState(initialUserState)
  const [ isLoading, setIsLoading ] = useState(false)
  const [ isError, setIsError ] = useState(false)

  useEffect(() => {
    async function getUserData() {
      setIsLoading(true);
      setIsError(false);

      const spotify = axios.create({
        baseURL: 'https://api.spotify.com/v1/me',
        headers: {
          'Authorization': `Bearer ${token}`
        }
      })
      try {
        const userRes = await spotify('/')
        .then( res => { return res.data });

        setUserData({
          display_name: userRes.display_name,
          external_url: userRes.external_urls.spotify,
          images: userRes.images[0].url
        })
      } catch (error) {
          setIsError(true)
      }
      setIsLoading(false);
    }
    getUserData();
  }, [])

  const data = {
    userData,
    isLoading,
    isError
  }
  return data
}

Parent hook 父母钩

export default function Home() {

  const  token = useParseToken()
  const { userData, isLoading, isError } = useFetchUserData(token);

  if (isLoading) return <BarLoader />;
  if (isError) return <div>Oops! something went wrong</div>;

  return (
    <Fragment>
      <Header userData={userData}/>
    </Fragment>  
  )   
}

You have to use createContext api of react. 您必须使用react的createContext api。 Save your token as a context. 将令牌另存为上下文。 and use it where ever you want. 并在您想要的任何地方使用它。 I think this repository will help you. 我认为该存储库将为您提供帮助。

What happens in your case is that you are setting a state in useEffect hook in your custom hook to set token. 在这种情况下,您将在自定义钩子中的useEffect钩子中设置状态以设置令牌。 However you return the token from this hook without waiting for the effect to run, so the first time your useFetchUserData hook is called, it will receive empty string as a token. 但是,您可以从该挂钩返回令牌,而无需等待效果运行,因此,第一次调用useFetchUserData挂钩时,它将接收空字符串作为令牌。 To solve this, you must implement the useFetchUserData hook to run once token is available or it changed 为了解决这个问题,您必须实现useFetchUserData挂钩,以在令牌可用或令牌更改后立即运行

export default function useFetchUserData(token) {
  //state
  const initialUserState = {
    display_name: '',
    external_url: '',
    images: ''
  }
  const [ userData, setUserData ] = useState(initialUserState)
  const [ isLoading, setIsLoading ] = useState(false)
  const [ isError, setIsError ] = useState(false)

  useEffect(() => {
    async function getUserData() {
      setIsLoading(true);
      setIsError(false);

      const spotify = axios.create({
        baseURL: 'https://api.spotify.com/v1/me',
        headers: {
          'Authorization': `Bearer ${token}`
        }
      })
      try {
        const userRes = await spotify('/')
        .then( res => { return res.data });

        setUserData({
          display_name: userRes.display_name,
          external_url: userRes.external_urls.spotify,
          images: userRes.images[0].url
        })
      } catch (error) {
          setIsError(true)
      }
      setIsLoading(false);
    }
    if(token !== '' || token !== null) {
       getUserData();
    }
  }, [token])

  const data = {
    userData,
    isLoading,
    isError
  }
  return data
}

Also since useParseToken returns the token, you don't need to destructure it while using 另外,由于useParseToken返回令牌,因此您在使用时无需对其进行解构

const token = useParseToken();

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM