Как заставить React перерисовать все компоненты на странице из массива array.map? - PullRequest
0 голосов
/ 14 апреля 2020

У меня есть родительский компонент (Game), который отображает дочерние элементы (Card) из массива. У меня также есть компонент Menu, который выполняет обратный вызов Game, чтобы изменить его состояние. Когда я меняю уровни (нажатие кнопки из Меню), я хочу, чтобы все текущие карты исчезали, а затем появлялись вместе с новыми, чтобы они имитировали c постепенное появление CSS, примененное к вновь отрендеренным картам. Я пробовал forceUpdate в нескольких местах и ​​проходил фиктивные операции, чтобы проверить наличие изменений. По сути, я хочу, чтобы все карты, находящиеся в данный момент на странице, отображались заново, в то время как страница отображает новые, когда это необходимо.

Вот фрагмент из игры:

{this._gameArray.map( (item, i) => {
  let dummyProp = Math.random();
  return <Card key={i}
           ...
           dummyProp={dummyProp}/>
})}

и фрагмент из карты :

componentWillReceiveProps(nextProps) {
  ...
  if (nextProps.dummyProp !== this.props.dummyProps) {
    this.forceUpdate();
  }
}

Я использую componentWillReceiveProps для другой цели и решил попробовать его для проверки изменения dummyProp. Новые карты исчезают, но предыдущие ничего не делают.

Ответы [ 2 ]

0 голосов
/ 14 апреля 2020

Вы можете просто попробовать другой трюк. Если вы хотите, чтобы все компоненты выполняли рендеринг каждый раз при изменении уровня, вы можете использовать логическое значение, установить его в значение false, когда вы начнете изменение, и установить его в значение true, когда вы закончите sh изменение / через некоторое время. Затем добавьте условие для рендеринга элементов.

{shouldRender && this._gameArray.map( (item, i) => {
  let dummyProp = Math.random();
  return <Card key={i}
           ...
           dummyProp={dummyProp}/>
})}

, когда shouldRender имеет значение true, карты будут отображаться, а когда значение false, они не будут. Таким образом, каждый раз будет верно, что все они будут перерисованы, и вы получите эффект.

0 голосов
/ 14 апреля 2020

React имеет несколько алгоритмов для определения того, какие компоненты должны быть перерисованы, в вашем случае React не будет перерисовывать компонент, потому что вы используете индексы, так как ключи и индексы одинаковы. Вам необходимо обновить key между повторными рендерингами.

В качестве простейшего решения вы можете использовать индекс + некоторый след в ключе, что-то вроде этого:

<Card key={index + '-' + Date.now()} card={card}/>;

В этом случае , компонент <Card/> будет отображаться каждый раз, когда состояние компонента <Game/> меняется.

class Card extends React.Component {
            constructor(props) {
                super(props)
            }

            render() {
                return (
                    <div class="card">Card {this.props.card.name}</div>
                )
            }
        }

        class Game extends React.Component {
            constructor(props) {
                super(props)

                this.state = {
                    counter: 0,
                    cards: [
                        { name: 'Card1' },
                    ]
                }
            }

            increaseCounter = () => {
                this.setState({ counter: this.state.counter + 1 })
            }

            render() {
                return (
                    <div>
                        <h2 onClick={this.increaseCounter}>Click me: {this.state.counter}</h2>
                        <h3>Will be re-rendered</h3>
                            {this.state.cards.map((card, index) => {
                                return <Card key={index + '-' + Date.now()} card={card} />;
                            })}
                        <h3>Won't be re-rendered</h3>
                            {this.state.cards.map((card, index) => {
                                return <Card key={index} card={card} />;
                            })}
                    </div>
                )
            }
        }

        ReactDOM.render(<Game />, document.querySelector("#app"))
body {
  background: #20262E;
  padding: 20px;
  font-family: Helvetica;
}

#app {
  background: #fff;
  border-radius: 4px;
  padding: 20px;
  transition: all 0.2s;
}

h2 {
  font-weight: bold;
  margin-bottom: 15px;
}

.card{
  animation: fade-in-out 3000ms ease-in-out forwards; 
}

  @keyframes fade-in-out {
    0% {
      background-color: transparent;
    }
     50% {
      background-color: red;
    }
     100%{
      background-color: transparent;
    }
  }
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.6.3/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.6.3/umd/react-dom.production.min.js"></script>
<div id="app"></div>

НО, для реальных приложений я бы порекомендовал вам рассмотреть возможность использования некоторой библиотеки анимации React, такой как response-transition- группа , потому что, если вы анимируете карты только с CSS, может произойти некоторое мигание, если повторный рендеринг произойдет быстрее, чем время ожидания анимации.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...