将子级用作函数时组件不呈现
Posted
技术标签:
【中文标题】将子级用作函数时组件不呈现【英文标题】:Component not rendering when using children as function 【发布时间】:2020-06-08 12:40:18 【问题描述】:我正在使用react-apollo
查询graphQL 服务器,并能够成功地为客户端提供数据。由于会有多个地方查询数据,因此我将尝试创建一个容器(重构)来封装useQuery
挂钩,以便可以在一个地方使用它。
第一次尝试(按预期工作)
const HomeContainer = () =>
const data, error, loading = useQuery(GET_DATA_QUERY,
variables: DATA_VARIABLES
);
const [transformedData, setTransformedData] = useState();
useEffect(() =>
if(!!data)
const transformedData = someTransformationFunc(data);
setTransformedData(...transformedData);
, [data]);
if (loading)
return <div>Loading data ...</div>;
if (error)
return <p>Error loading data</p>;
if (!data)
return <p>Not found</p>;
return <Home transformedData=transformedData />;
;
我想将围绕查询的不同阶段的仪式封装到一个新容器中(加载、错误状态),这样我就可以减少代码重复。
第一次尝试重构
查询容器在query
、variables
和callback
中传递。这负责根据查询的状态(加载、错误或没有数据返回时)返回不同的节点。
const HomeContainer = () =>
const data, error, loading = useQuery(GET_DATA_QUERY,
variables: DATA_VARIABLES
);
const [transformedData, setTransformedData] = useState();
const callback = (data) =>
const transformedData = someTransformationFunc(data);
setTransformedData(...
transformedData
);
;
return (
<QueryContainer
query=GET_DATA_QUERY
variables=DATA_VARIABLES
callback =callback
>
<Home transformedData=transformedData />
</QueryContainer>
)
;
const QueryContainer = (callback, query, variables, children ) =>
const data, error, loading = useQuery(query,
variables: variables
);
// Once data is updated invoke the callback
// The transformation of the raw data is handled by the child
useEffect(() =>
if (!!data)
callback(data);
, [data]);
if (loading)
return <div > Loading data... < /div>;
if (error)
return <p > Error loading data < /p>;
if (!data)
return <p > Not found < /p>;
return children;
;
QueryContainer
正在使用useEffect
并在data
返回时调用callback
。我觉得这有点混乱,违背了封装在父级中并使用callback
与子级交谈和更新的目的。
第三次尝试(使用子函数)
摆脱回调并将数据作为第一个参数传递给children
函数。
const HomeContainer = () =>
return (
<QueryContainer
query=GET_DATA_QUERY
variables=DATA_VARIABLES
>
(data) =>
const transformedData = someTransformationFunc(data);
return <Home transformedData=transformedData />;
</QueryContainer>
)
;
const QueryContainer = ( query, variables, children ) =>
const data, error, loading = useQuery(query,
variables: variables
);
if (loading)
return <div>Loading data ...</div>;
if (error)
return <p>Error loading data</p>;
if (!data)
return <p>Not found</p>;
return children(data);
;
我希望这会起作用,因为没有真正改变,并且当 data
更新时,新的渲染会以 data
作为参数调用子函数。
但是当我导航到那条路线时,我看到一个黑屏(没有错误,我可以看到登录到控制台的正确数据)
如果我再次单击该链接,我可以看到提交给 DOM 的组件。
不太确定这里发生了什么,想知道是否有人可以阐明这里发生了什么。
【问题讨论】:
【参考方案1】:我在上面添加的代码 sn-ps 在隔离时按预期工作。
https://codesandbox.io/s/weathered-currying-4ohh3
问题出在层次结构树下的某个其他组件导致组件无法重新渲染。
第二个实现按预期工作,因为组件由于从父级调用的回调而再次呈现。
【讨论】:
【参考方案2】:嗯,应该可以了……
试试这样的(组件注入,有点像 HOC - inspiration):
const HomeContainer = () =>
return (
<QueryContainer
query=GET_DATA_QUERY
variables=DATA_VARIABLES
transformation=someTransformationFunc
renderedComponent=Home
/>
)
;
const QueryContainer = ( query, variables, transformation, renderedComponent: View ) =>
const data, error, loading = useQuery(query, variables );
if (loading)
return <div>Loading data ...</div>;
if (error)
return <p>Error loading data</p>;
if (!data)
return <p>Not found</p>;
// let transformedData = transformation(data);
// return <View transformedData=transformedData />;
return <View transformedData=transformation ? transformation(data) : data />;
;
如果仍然无法正常工作 (!?),请将 data
和 transformation
作为道具传递并使用它们来初始化状态(使用 useState
或 useEffect
)。
你真的/仍然需要<HomeContainer/>
作为抽象吗? ;)
【讨论】:
Do you really/still need <HomeContainer/> as an abstraction?
不是真的,我只是想提出一个简约的例子。俱乐部 useEffect
或 useState
肯定会起作用(第二个示例使用它)。我试图了解可能会发生什么,因为当使用 child 作为函数时它没有被正确渲染
我的意思是 <Home/>
const someState, setSomeState= useState(transformation(data));
内的 1-liner ...我的解决方案有效吗?你需要知道为什么你不这样做?
我没有检查您的解决方案是否有效。但从技术上讲,impl与我在问题中的第三个非常相似,children as a func vs HOC
当然,工作与不工作之间的差异可能很小;)
发现问题。实施按预期工作。不幸的是,问题出在其他一些有条件地渲染容器的组件上,因此这似乎不起作用。它适用于前 2 个实现,因为 setState
被调用。这是工作示例 -codesandbox.io/s/weathered-currying-4ohh3以上是关于将子级用作函数时组件不呈现的主要内容,如果未能解决你的问题,请参考以下文章
将函数提取到独立的 reactjs 组件中会抛出对象作为 React 子级无效(找到:[object Window])