首页 > 其他 > 详细

[React] Refactor a render prop component into hooks

时间:2020-04-30 09:05:15      阅读:54      评论:0      收藏:0      [点我收藏+]

Our <Query /> component is a render prop based component that the <User /> component uses. But because it doesn‘t render anything, we can actually just change it to a custom hook. Let‘s create a useQuery hook that returns the state from the hooks the Query component uses and use that instead. But we‘ll preserve the component so we don‘t have to refactor everywhere that uses the Query render prop based component as well and we can keep our tests passing as they are.

function Query({query, variables, normalize = data => data, children}) {
  const client = useContext(GitHub.Context)
  const [state, setState] = useSafeSetState({
    loaded: false,
    fetching: false,
    data: null,
    error: null,
  })

  useDeepCompareEffect(
    () => {
      setState({fetching: true})
      client
        .request(query, variables)
        .then(res =>
          setState({
            data: normalize(res),
            error: null,
            loaded: true,
            fetching: false,
          }),
        )
        .catch(error =>
          setState({
            error,
            data: null,
            loaded: false,
            fetching: false,
          }),
        )
    },
    [query, variables],
  )

  return children(state)
}

function User({username}) {
  const {logout} = useContext(GitHubContext)
  const [filter, setFilter] = useState(‘‘)
  return (
    <Query
      query={userQuery}
      variables={{username}}
      normalize={normalizeUserData}
    >
      {({fetching, data, error}) =>
        error ? (
          <IsolatedContainer>
            <p>There was an error loading the data</p>
            <pre>{JSON.stringify(error, null, 2)}</pre>
          </IsolatedContainer>
        ) : fetching ? (
          <LoadingMessagePage>Loading data for {username}</LoadingMessagePage>
        ) : data ? (
          <UserContext.Provider value={data}>
            <Container>
              <Row>
                <Column width="3">
                  <Profile />
                  <PrimaryButton
                    css={{marginTop: 20, width: ‘100%‘}}
                    onClick={logout}
                  >
                    Logout
                  </PrimaryButton>
                  <ButtonLink css={{marginTop: 20, width: ‘100%‘}} to="/">
                    Try another
                  </ButtonLink>
                </Column>
                <Column width="9">
                  <Text size="subheading">Repositories</Text>
                  <RepoFilter filter={filter} onUpdate={setFilter} />
                  <RepoList filter={filter} />
                </Column>
              </Row>
            </Container>
          </UserContext.Provider>
        ) : (
          <IsolatedContainer>I have no idea what‘s up...</IsolatedContainer>
        )
      }
    </Query>
  )
}

 

Refactor code into hooks:

function useQuery({query, variables, normalize = data => data}) {
  const client = useContext(GitHub.Context)
  const [state, setState] = useSafeSetState({
    loaded: false,
    fetching: false,
    data: null,
    error: null,
  })

  useDeepCompareEffect(
    () => {
      setState({fetching: true})
      client
        .request(query, variables)
        .then(res =>
          setState({
            data: normalize(res),
            error: null,
            loaded: true,
            fetching: false,
          }),
        )
        .catch(error =>
          setState({
            error,
            data: null,
            loaded: false,
            fetching: false,
          }),
        )
    },
    [query, variables],
  )

  return state
}

const Query = ({children, ...props}) => children(useQuery(props))

 

We add API ‘useQuery‘ it just return state, instead of rendering the children. And we still keep ‘Query‘ component in case somewhere in the code, we still need to use render prop partten.

function User({username}) {
  const {logout} = useContext(GitHubContext)
  const [filter, setFilter] = useState(‘‘)

  const {fetching, data, error} = useQuery({
    query: userQuery,
    variables: {username},
    normalize: normalizeUserData,
  })

  return error ? (
    <IsolatedContainer>
      <p>There was an error loading the data</p>
      <pre>{JSON.stringify(error, null, 2)}</pre>
    </IsolatedContainer>
  ) : fetching ? (
    <LoadingMessagePage>Loading data for {username}</LoadingMessagePage>
  ) : data ? (
    <UserContext.Provider value={data}>
      <Container>
        <Row>
          <Column width="3">
            <Profile />
            <PrimaryButton
              css={{marginTop: 20, width: 100%}}
              onClick={logout}
            >
              Logout
            </PrimaryButton>
            <ButtonLink css={{marginTop: 20, width: 100%}} to="/">
              Try another
            </ButtonLink>
          </Column>
          <Column width="9">
            <Text size="subheading">Repositories</Text>
            <RepoFilter filter={filter} onUpdate={setFilter} />
            <RepoList filter={filter} />
          </Column>
        </Row>
      </Container>
    </UserContext.Provider>
  ) : (
    <IsolatedContainer>I have no idea whats up...</IsolatedContainer>
  )
}

 

[React] Refactor a render prop component into hooks

原文:https://www.cnblogs.com/Answer1215/p/12806084.html

(0)
(0)
   
举报
评论 一句话评论(0
关于我们 - 联系我们 - 留言反馈 - 联系我们:wmxa8@hotmail.com
© 2014 bubuko.com 版权所有
打开技术之扣,分享程序人生!