Как структурировать код React с помощью Apollo Query, чтобы случайный запрашиваемый массив объектов не изменял представление при изменении состояния? - PullRequest
0 голосов
/ 08 января 2019

Проблема : я использую Apollo Client, и мне колоду воспроизводят так: «/ deck / 2», и я хочу случайным образом перетасовать карты и отображать только одну по одному с Кнопка для просмотра следующего. Я продолжаю работать с проблемой повторного рендеринга React при каждом изменении состояния (мой счетчик индекса onClick +1), который перетасовывает карты, поскольку переменная shuffledCards находится внутри запроса. Я не уверен, как этого избежать.

Как я могу перетасовать список, не беспокоясь о том, что они перетасовываются при нажатии кнопки «onClick». Я предполагаю, что есть способ получить рандомизированный массив за пределами рендера, что я могу сделать в Regular реагировать, но используя запросы Apollo, я не могу понять.

Это то место, где я спотыкаюсь из-за моей неопытности в React и Graphql с Apollo, и я не нашел аналогичного проекта Apollo graphql, на который можно было бы опираться. Я не могу использовать карту на объекте, но, может быть, есть способ использовать карту для отображения 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 голосов
/ 10 февраля 2019

Вы вызываете this.shuffle () в вашей функции рендеринга - поэтому он будет перетасовываться при каждом рендере.

Переместите это в свой конструктор, и он будет вызван только один раз.

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

  this.goToNext = this.goToNext.bind(this);
  const CardsToRender = data.deck.cards;
}
0 голосов
/ 08 января 2019

Мне не известно о 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>
        )
    }
}
...