如何使用Apollo Query构造React代码,以使对象的随机查询数组不会因状态变化而重新呈现?

时间:2019-01-08 06:18:52

标签: reactjs graphql apollo react-apollo

问题:我正在使用Apollo Client,并以“ / deck / 2”这样的形式呈现了牌组,我想随机洗牌,并一次只显示一张一个按钮以查看下一个。每当状态改变时,我就一直在使用React重新渲染(我的onClick索引计数器+1),这一直困扰着我,因为shuffledCards变量在查询中,所以它会重新洗牌。我不确定如何防止这种情况发生。

我如何才能重新整理列表,而不必担心会在按钮的“ onClick”上重新整理它们。我想有一种方法可以在渲染之外获取随机数组,我可以在Regular反应中进行此操作,但是我很想了解使用Apollo查询的方法。

这是我在React和Graphql上对Apollo缺乏经验的绊脚石,而我还没有找到类似的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>
        );
      }
    }

    ```

我将感谢您提供的任何帮助。谢谢!

2 个答案:

答案 0 :(得分:0)

我不了解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>
        )
    }
}

答案 1 :(得分:0)

您在渲染函数中调用this.shuffle()-因此它将在每个渲染器上随机播放。

将其移到构造函数中,它只会被调用一次。

constructor(props) {
  super(props);
  this.state = {
    index: 0
  };

  this.goToNext = this.goToNext.bind(this);
  const CardsToRender = data.deck.cards;
}