一段时间以来,这一直是我团队中几个人之间的争论点。
在我们的应用程序中,我们使用 apollo graphql 对我们的 API 进行异步查询/更改。
也就是说,在过去,已经有一些决定使用lodash/fp
的get
来设置这样的变量:
import { graphql } from '@apollo/hoc';
import { get } from 'lodash/fp';
const Component = (props) => {
const data = get('data.user', 'props');
return (
<>
{data.member.name}
<OtherComponent data={get('data.member.whatever.data', 'props')} />
</>
)
}
export default compose(
graphql(MY_QUERY, {
options: { variables: { id: 'abc123dorime' } }
}), // async data
)(Component)
根据我继承此应用程序的经验,这很容易出错。有时它会导致错误,有时不会;基本上是一种竞争条件。我的理论是:如果 react 渲染速度快于解析速度,它会使应用程序崩溃。未处理加载状态,也没有错误处理。但是,我对此不是 100% 确定,因为我真的不知道get
如何完全在引擎盖下工作。此特定示例的错误是此处undefined
user
。有时。在我们开发时,我通常会很好,但是我们的用户在使用get
的页面上报告了错误。有时!!!
但是,我决心放弃使用get
来解决异步数据。我的方法是移动到 apollo 的钩子,并实际使用它提供的loading
状态在渲染之前解析异步数据:
import { useQuery, useMutation } from '@apollo/react-hooks';
const Component = () => {
const { data, loading, error } = useQuery(MY_QUERY, {
variables: { id: '123abcdorime' },
});
const [ myMutation, { loading: mutationLoading } ] = useMutation(MY_MUTATION, {
variables: { id: '123', input: { name: 'whatever' } },
onCompleted: () => doSomething(),
onError: (err) => handleError(err),
});
return (
<>
{loading && <LoadingCircle />}
{!loading && (
<>
{error && <ErrorHandler error={error} />}
{!error && <>data.user.member.name</>}
</>
)}
</>
);
}
export default Component;
这样,它可以在逻辑上考虑loading
状态并正确解析,并在不使用的情况下正确呈现get
,重构为后一种模式倾向于解决get
或getOr
倾向于在我们的生产应用程序中给我们带来的异步问题。
有些观点是,lodash/fp
get
提供了空检查,将编码防御性地付诸实践,但后者在逻辑上不是也在使用错误处理/加载状态进行防御性编码吗?
我想我最大的问题是:
get
或getOr
什么时候是一个好的用例?- 。它们对于处理异步数据有用吗?
这纯粹是出于好奇。如前所述,这是我和我的团队之间的一个巨大争论点,我们应用程序中的大多数问题都源于get
和异步数据的使用。我只想知道我是否误解了什么,或者我/我们是否滥用了get
/getOr
.
get
根本不与异步相关...您应该始终保护任何data
属性/字段访问(分配给其他常量/变量,作为 prop 传递(。
const data = get('data.user', 'props'(;
。这是一点保护 - "正常"const data = props.data.user;
如果不加载data
会失败("著名"反应错误"Cannot read property 'XXXXXXX' of undefined"
(
{data.member.name}
。应该受到保护,即通过{props.data && data.member.name}
。这不会失败,但可能会导致意外的data
值(undefined
(作为prop传递,而不是预期/获取的值。
没有<Loading/>
就足够了if(!props.data) return null;
(当然在呈现适当的内容之前(
return (
<>
{loading && <LoadingCircle />}
{!loading && (
你可以简单地
// '!data' is equal to 'loading' in this case
if(loading || mutationLoading) return <LoadingCircle />
if(error) return <ErrorHandler error={error} />
return (
<>data.user.member.name</>
)