如何使用 Apollo Query 构造 React 代码,以便随机查询的对象数组不会随着状态变化而重新呈现?
Posted
技术标签:
【中文标题】如何使用 Apollo Query 构造 React 代码,以便随机查询的对象数组不会随着状态变化而重新呈现?【英文标题】:How to structure React code with Apollo Query so that a random queried array of objects doesn't rerender with state change? 【发布时间】:2019-06-02 19:31:35 【问题描述】:问题:我正在使用 Apollo 客户端,并且将牌组渲染为这样的“/deck/2”,我想随机洗牌,一次只显示一张一个按钮查看下一个。每次状态更改时,我都会遇到 React 重新渲染的问题(我的 onClick 索引计数器 +1),这会重新洗牌,因为 shuffledCards 变量在查询中。我不确定如何防止这种情况发生。
我怎样才能改变列表而不用担心它们会被按钮的“onClick”重新洗牌。我想有一种方法可以在渲染之外获取随机数组,我可以在常规反应中做到这一点,但是使用 Apollo 查询我很难理解。
由于我对 Apollo 的 React 和 Graphql 缺乏经验,这就是我遇到的问题,而且我还没有找到类似的 Apollo graphql 项目可以借鉴。我不能在对象上使用 map,但也许有一种方法可以使用 map 一次显示数组的 1 个对象?我还没有找到可行的解决方案。
我打算发生的事情:我只是想一次渲染一张洗牌的卡片数组,然后按下下一步按钮应该逐步浏览随机数组中的卡片,而无需重新每当我点击按钮时渲染,否则卡片将随机重复。
代码如下:
import React, Component, Fragment from "react";
```
import CardItem from "./CardItem";
const CARDS_QUERY = gql`
query CardsQuery($id: ID!)
```
`;
export class Cards extends Component
constructor(props)
super(props);
this.state =
index: 0
;
this.goToNext = this.goToNext.bind(this);
goToNext()
this.setState(
index: this.state.index + 1
);
shuffle(array)
for (let i = array.length - 1; i > 0; i--)
const j = Math.floor(Math.random() * (i + 1));
[array[i], array[j]] = [array[j], array[i]];
return array;
render()
let id = this.props.match.params;
id = parseInt(id);
return (
<Fragment>
<Query query=CARDS_QUERY variables= id >
( data, error, loading ) =>
if (loading)
return <Loading />;
if (error)
const CardsToRender = data.deck.cards;
//This variable gets reshuffled at every re-render
const shuffledCards = this.shuffle(CardsToRender);
//Single item to be returned
const item = shuffledCards[this.state.index];
if (this.state.index >= shuffledCards.length)
return (
<div>
<h1>Finished</h1>
</div>
);
else
return (
<Fragment>
// Here I can get one item to display, but if I press next, the state changes which fires a re-render,
//shuffling the cards once more. My intention is to only shuffle cards at first render until the browser page is
//refreshed or user navigates away
<h1>item.front</h1>
<h1>item.back</h1>
//My second attempt is to map out the cards, but I have only been able to render a list,
// but not one at a time. Maybe there is a simple code solution in my .map to display
//one at a time without needing to change state?
shuffledCards.map(card => (
<CardItem key=card.id card=card />
))
<p>
<button onClick=this.goToNext>Next</button>
</p>
</Fragment>
);
</Query>
</Fragment>
);
```
我将不胜感激提供的任何帮助。谢谢!
【问题讨论】:
【参考方案1】:您在渲染函数中调用 this.shuffle() - 因此它会在每次渲染时随机播放。
将它移到你的构造函数中,它只会被调用一次。
constructor(props)
super(props);
this.state =
index: 0
;
this.goToNext = this.goToNext.bind(this);
const CardsToRender = data.deck.cards;
【讨论】:
【参考方案2】:我不知道 Appolo Query,但您提到的问题与 React 更相关。您可以尝试以下一种,以避免在每次重新渲染时洗牌。
将您的组件重构为两部分。
1) ShuffleCards.js(随便取个名字:) ) - 将你的组件移动到“ShuffleCards”中,然后将打乱的绳索传递给子组件,在那里你可以更新状态以呈现下一张卡片。
// ShuffledCards.js
import React, Component, Fragment from "react";
```
import CardItem from "./CardItem";
const CARDS_QUERY = gql`
query CardsQuery($id: ID!)
```
`;
export class ShuffleCards extends Component
constructor(props)
super(props);
shuffle(array)
for (let i = array.length - 1; i > 0; i--)
const j = Math.floor(Math.random() * (i + 1));
[array[i], array[j]] = [array[j], array[i]];
return array;
render()
let id = this.props.match.params;
id = parseInt(id);
return (
<Fragment>
<Query query=CARDS_QUERY variables= id >
( data, error, loading ) =>
if (loading)
return <Loading />;
if (error)
const CardsToRender = data.deck.cards;
const shuffledCards = this.shuffle(CardsToRender);
return (
<Cards shuffledCards=shuffledCards />
);
</Query>
</Fragment>
);
将处理显示卡片和更新状态的代码移至“卡片”组件。
import React, Component, Fragment from "react";
import CardItem from "./CardItem";
export class Cards extends Component
constructor(props)
super(props);
this.state =
index: 0
;
this.goToNext = this.goToNext.bind(this);
goToNext()
this.setState(
index: this.state.index + 1
);
render()
const shuffledCards = this.props || [];
return (
<div>
this.state.index >= shuffledCards.length ?
<div>
<h1>Finished</h1>
</div>
:
<Fragment>
<h1>item.front</h1>
<h1>item.back</h1>
shuffledCards.map(card => (
<CardItem key=card.id card=card />
))
<p>
<button onClick=this.goToNext>Next</button>
</p>
</Fragment>
</div>
)
【讨论】:
以上是关于如何使用 Apollo Query 构造 React 代码,以便随机查询的对象数组不会随着状态变化而重新呈现?的主要内容,如果未能解决你的问题,请参考以下文章
NextJS + Apollo:如何在 getServerSideProps 内的 apolloClient.query 上获取多个查询?
如何从 Apollo 缓存中查询没有 ROOT_QUERY 上的查询
使用 Redux Thunk 和 Apollo Graphql 做出反应 - 如何访问 client.query?