React - Использование тернарного кода для применения класса CSS в функциональном компоненте

Я относительно новичок в React и работаю над приложением John Conway - Game of Life. Я построил Gameboard.js функциональный компонент для самой платы (который является дочерним элементом App.js) и Square.js функциональный компонент, который представляет собой отдельный квадрат на доске (и является дочерним элементом Gameboard и внуком App).

В App у меня есть функция alive, которую я хочу изменить цвет отдельного квадрата, когда пользователь щелкает по нему. App также имеет свойство «жив» в состоянии, изначально заданном как false, а alive изменит свойство на true при вызове.

Вот App.js:

import React, { Component } from 'react';
import './App.css';
import GameBoard from './GameBoard.js';
import Controls from './Controls.js';

    class App extends Component {
      constructor(props){
        super(props);

        this.state = {
          boardHeight: 50,
          boardWidth: 30,
          iterations: 10,
          reset: false,
          alive: false
        };
      }

      selectBoardSize = (width, height) => {
        this.setState({
          boardHeight: height,
          boardWidth: width
        });
      }

      onReset = () => {

      }

      alive = () => {
        this.setState({ alive: !this.state.alive });
        console.log('Alive function has been called');

      }



      render() {
        return (
          <div className="container">
            <h1>Conway's Game of Life</h1>

          <GameBoard
            height={this.state.boardHeight}
            width={this.state.boardWidth}
            alive={this.alive}
          />

            <Controls
              selectBoardSize={this.selectBoardSize}
              iterations={this.state.iterations}
              onReset={this.onReset}
            />

          </div>
        );
      }
    }

    export default App;

Gameboard выглядит так и передает props.alive Square:

import React, { Component } from 'react';
import Square from './Square.js';

const GameBoard = (props) => {
    return (
      <div>
        <table className="game-board">
          <tbody>
            {Array(props.height).fill(1).map((el, i) => {
              return (
                <tr key={i}>
                  {Array(props.width).fill(1).map((el, j) => {
                    return (
                      <Square key={j} alive={props.alive}/>
                    );
                  })}
                </tr>
              );
            })}
          </tbody>
         </table>
      </div>
    );
}

export default GameBoard;

В моем CSS у меня есть класс под названием active, который меняет цвет отдельного квадрата, если по нему щелкнуть. Как я могу сделать так, чтобы в Square, если щелкнуть элемент td, цвет изменился (т.е. классы CSS были изменены на активные)?

Я пробовал это:

import React, { Component } from 'react';

const Square = (props) => {

  return(
    <td className={props.alive ? "active" : "inactive"} onClick={() => props.alive()}></td>
  );
}

export default Square;

CSS выглядит так:

.Square {
  background-color: #013243; //#24252a;
  height: 12px;
  width: 12px;
  border: .1px solid rgba(236, 236, 236, .5);
  overflow: none;

  &:hover {
    background-color: #48dbfb; //#00e640; //#2ecc71; //#39FF14;
  }
}

.inactive {
  background-color: #013243; //#24252a;
}

.active {
  background-color:  #48dbfb;
}

Как сделать так, чтобы класс CSS .Square ВСЕГДА применялся к каждому квадрату, но цвет отдельного квадрата менялся, если он активен? Другими словами, могу ли я установить для Square td всегда стиль с классом CSS .Square, а затем отдельные элементы внутри Square могут быть окрашены соответствующим образом в зависимости от того, истинно ли alive в состоянии App?

Есть ли тройной подход, чтобы всегда устанавливать один конкретный класс CSS, а затем, кроме того, устанавливать 1 из 2 других классов ... т.е. класс Square CSS всегда отображается, а активный или неактивный отображается в зависимости от логики / состояния?


person Nick Kinlen    schedule 19.09.2018    source источник
comment
Не работает, ошибка JSX. Мой подход здесь совершенно неправильный? Я чувствую, что должен быть общий способ относительно легко сделать что-то подобное.   -  person Nick Kinlen    schedule 19.09.2018
comment
так жаль. Я испортил . Это должно быть похоже на className = {'Square $ {props.alive? active: inactive} '} - использовать обратные кавычки вместо одинарных кавычек   -  person Shubham Yerawar    schedule 19.09.2018
comment
Ошибок больше нет, но класс CSS .Square не применяется / доска не отображается.   -  person Nick Kinlen    schedule 19.09.2018


Ответы (3)


Комментарии имеют правильное представление.

Вы можете использовать литерал шаблона и встроить тернарные условные выражения в этом:

return (
    <td
      className={`Square ${props.alive ? "active" : "inactive"}`}
      onClick={() => props.alive()}
    ></td>
);

Краткий обзор литералов шаблонов: используйте обратные кавычки, чтобы обернуть строку, и вы можете вставить выражение JavaScript внутри него, заключив его в шаблон ${}. В качестве бонуса литералы шаблона могут охватывать несколько строк, поэтому больше не будет неудобной конкатенации строк!

const myName = "Abraham Lincoln";
const myString = `Some text.
  This text is on the next line but still in the literal.
  Newlines are just fine.
  Hello, my name is ${myName}.`;

Изменить: более серьезная проблема, которую я сейчас вижу, заключается в том, что вы нигде не сохраняете состояние каждой своей ячейки. У вас есть только одно логическое значение, хранящееся в App и называемое _5 _... вам действительно нужен массив логических значений, каждое из которых представляет состояние одного Square.

Массив «живых» состояний должен находиться в App или GameBoard, следуя принципу React " данные текут вниз ". В вашем случае вы можете попробовать оставить его в App, и тогда GameBoard и Square могут оставаться чисто функциональными компонентами.

Внутри App вы можете создать новый двумерный массив board в конструкторе и изначально заполнить его подмассивами со значениями 0:

// App.js
constructor(props){
    super(props);

    this.state = {
      boardHeight: 50,
      boardWidth: 30,
      board: [],
      iterations: 10,
      reset: false,
    };

    this.state.board = new Array(this.state.boardHeight).fill(new Array(this.state.boardWidth).fill(0));
  }

В массиве board каждый индекс представляет одну строку. Таким образом, упрощенный пример [[0, 0, 1], [0, 1, 0], [1, 1, 1]] будет представлять:

0 0 1
0 1 0
1 1 1

GameBoard должен отображать вашу сетку ячеек исключительно на основе board переданного ему пропа и передавать каждому Square его живое значение и функцию обратного вызова в качестве реквизита:

const GameBoard = (props) => {
    return (
      <div>
        <table className="game-board">
          <tbody>
            {this.props.board.map((row, y) => {
              return <tr key={y}>
                {row.map((ea, x) => {
                  return (
                    <Square
                      key={x}
                      x={x}
                      y={y}
                      isAlive={ea}
                      aliveCallback={this.props.alive}
                    />
                  );
                })}
              </tr>;
            })}
          </tbody>
         </table>
      </div>
    );
}

Оттуда вы сможете увидеть, как это приложение будет работать. App сохраняет состояние игры и отображает функциональный компонент GameBoard. В GameBoard каждый Square отображается в соответствии с его активным значением и запускает aliveCallback при нажатии. aliveCallback должен установить состояние соответствующего значения в массиве board внутри App на основе его x и y prop.

person jered    schedule 19.09.2018
comment
Литерал шаблона компилирует и применяет активный класс, но вся доска изменяется на активный цвет, а не только на отдельные квадраты, по которым щелкнули мышью. Я хочу, чтобы нажатые квадраты применяли .active, а все остальные квадраты оставались такими же цветными. - person Nick Kinlen; 19.09.2018
comment
Могу ли я заменить Square.js на компонент класса и запоминать, активен ли каждый квадрат в компоненте, вместо того, чтобы передавать его в качестве реквизита? - person Nick Kinlen; 19.09.2018
comment
@NickKinlen Обновил мой ответ более подробно. :) - person jered; 20.09.2018
comment
Спасибо. Это хороший совет. Я знал, что применяемый мной подход не будет окончательным, но я просто сосредоточился на том, чтобы заставить работать функциональность щелчка. Этот совет определенно проясняет следующий мой вопрос. - person Nick Kinlen; 21.09.2018

Вы можете сделать как

return(
    <td className={`Square ${props.alive ? "active" : "inactive"}`} 
       onClick={() => props.alive()}>
    </td>
  );

См. Этот код

person Shubham Yerawar    schedule 19.09.2018
comment
При этом каждый квадрат на доске становится активным или неактивным. Я хочу, чтобы только активные квадраты применялись к активному цвету, а для остальных квадратов применялись неактивные цвета. - person Nick Kinlen; 19.09.2018
comment
используйте этот атрибут className в своем компоненте Square и передайте ему реквизиты как живые. Тогда это повлияет только на этот компонент. - person Shubham Yerawar; 19.09.2018
comment
Я не смог отформатировать свой ответ, поэтому использовал ‹backtick› вместо `. Хотя я поделился рабочей ссылкой своего кода. Там не должно быть никаких проблем - person Shubham Yerawar; 19.09.2018

Проблема с заголовком не являлась реальной причиной "неработоспособности"

ПРИМЕЧАНИЕ. Это заявление

className = {props.alive? "active": "inactive"}

правильно, использование шаблонных литералов не требуется.

Вы можете написать / использовать его разными способами:

className={'Square '+ (props.alive ? 'active' : 'inactive')}

По правде говоря, нет необходимости использовать «неактивный», поскольку «Квадрат» имеет тот же цвет bg.

className={'Square '+ (props.alive ? 'active' : null)}

и де-факто нет необходимости в тернарном операторе

className={'square '+ (props.alive && 'active')}

и, конечно, вы можете «рассчитать / подготовить» значения в простом js перед возвратом

const Square = (props) => {
  let classes = ['Square','bb']
  if( props.alive ) classes.push('active')
  classes = classes.join(' ')
  return (
    <h1 className={classes}>Hello</h1>
  )};

Просто прочтите docs или в Google, чтобы узнать, как «реагировать css в js».

person xadm    schedule 21.09.2018