Home > database >  N x N grid in React/TypeScript/Tailwind only works with hardcoded Tailwind class
N x N grid in React/TypeScript/Tailwind only works with hardcoded Tailwind class

Time:07-25

Working on making Tic-tac-toe with React TypeScript Tailwind CSS and dynamically filling in the grid

I was mostly done, but then I suddenly decided I wanted to parameterize the size of the grid to N x N instead of just 3 x 3

And I'm encountering something really strange now

Problem Overview

When I parameterize using "grid-cols-" NUM_ROWS_COLS.toString() the grid is often wrong (i.e. everything in one column), but when I hardcode with e.g. "grid-cols-5" or "grid-cols-3" then it works fine

Possible cause?

It might be an issue with my setup

My repo / branch is right here, but I will try to post some of the files directly

package.json

{
  "name": "tictactoe",
  "version": "0.1.0",
  "private": true,
  "dependencies": {
    "@testing-library/jest-dom": "^5.16.4",
    "@testing-library/react": "^13.3.0",
    "@testing-library/user-event": "^14.2.5",
    "@types/jest": "^28.1.6",
    "@types/node": "^17.0.45",
    "@types/react": "^18.0.15",
    "@types/react-dom": "^18.0.6",
    "autoprefixer": "^10.4.7",
    "postcss": "^8.4.14",
    "postcss-cli": "^9.1.0",
    "postcss-preset-env": "^7.7.2",
    "react": "^18.2.0",
    "react-dom": "^18.2.0",
    "react-scripts": "^5.0.1",
    "tailwindcss": "^3.1.6",
    "typescript": "^4.7.4"
  },
  "scripts": {
    "start": "react-scripts start",
    "build": "react-scripts build",
    "test": "react-scripts test",
    "eject": "react-scripts eject"
  },
  "eslintConfig": {
    "extends": "react-app"
  },
  "browserslist": {
    "production": [
      ">0.2%",
      "not dead",
      "not op_mini all"
    ],
    "development": [
      "last 1 chrome version",
      "last 1 firefox version",
      "last 1 safari version"
    ]
  }
}

postcss.config.js

module.exports = {
  plugins: {
    tailwindcss: {},
    "postcss-preset-env": {},
  },
};

tailwind.config.js

/** @type {import('tailwindcss').Config} */
module.exports = {
  theme: {
    extend: {},
  },
  plugins: [],
  content: ["./src/**/*.{js,jsx,ts,tsx,html}", "./public/index.html"],
};

tsconfig.json

{
  "compilerOptions": {
    "target": "es5",
    "lib": [
      "dom",
      "dom.iterable",
      "esnext"
    ],
    "allowJs": true,
    "skipLibCheck": true,
    "esModuleInterop": true,
    "allowSyntheticDefaultImports": true,
    "strict": true,
    "forceConsistentCasingInFileNames": true,
    "noFallthroughCasesInSwitch": true,
    "module": "esnext",
    "moduleResolution": "node",
    "resolveJsonModule": true,
    "isolatedModules": true,
    "noEmit": true,
    "jsx": "react-jsx"
  },
  "include": [
    "src"
  ]
}

src/index.tsx

import ReactDOM from 'react-dom/client'
import "./styles/tailwind.css";
import Board from "./App";
import React from 'react';

ReactDOM.createRoot(document.getElementById('root')!).render(
  <React.StrictMode>
    <Board />
  </React.StrictMode>
)

src/App.tsx

import * as React from 'react'
import * as ReactDOM from 'react-dom/client'

const NUM_ROWS_COLS: number = 4

interface SquareProps {
  top: boolean
  bottom: boolean
  left: boolean
  right: boolean
  row: number
  col: number
  parent: Board
  key: number
}

interface BoardState {
  squares: Array<SquareContainer>
}

interface SquareContainer {
  component: Square
  info: {value: string}
}

class Square extends React.Component<SquareProps, {}> {

  constructor(props: SquareProps) {
    super(props)
  }

  render() {
    let className: string = "aspect-square border-black p-0 m-0 font-sans text-center width-full height-full "

    className  = "col-span-1 row-span-1 "

    if (this.props.top) {
      className  = "border-t-2 " 
    }

    if (this.props.left) {
      className  = "border-l-2 "
    }

    if (this.props.bottom) {
      className  = "border-b-2 "
    }

    if (this.props.right) {
      className  = "border-r-2 "
    }

    return (
      <div className={className}></div>
    )
  }
}

export class Board extends React.Component<{}, BoardState> {

  constructor(props: {}) {
    super(props)
    let state: BoardState = {
      squares: new Array(Math.pow(NUM_ROWS_COLS, 2)).fill({component: null, info: {value: ""}}),
    }

    for (let row = 0; row < NUM_ROWS_COLS; row  ) {
      for (let col = 0; col < NUM_ROWS_COLS; col  ) {
        const b: boolean = (row === NUM_ROWS_COLS-1)
        const r: boolean = (col === NUM_ROWS_COLS-1)
        const square: SquareContainer = {
          component: new Square({
            top: true, left: true, bottom: b, right: r, row: row, col: col,
            parent: this, key: NUM_ROWS_COLS*row   col,
          }),
          info: {value: ""},
        }
        state.squares[NUM_ROWS_COLS*row   col] = square
      }
    }

    this.state=state
  }

  componentDidMount() {
    this.setState(this.state)
  }

  render() {
    //let style: string = `border-black border-x-0 border-y-0 grid `
    return (
      <div className={`grid grid-cols-${NUM_ROWS_COLS.toString()}`}>
        {this.state.squares.map((squareinfo: SquareContainer, index: number) => { 
          return squareinfo.component.render() 
        })}
      </div>
    );
  }
}

export default Board;

CodePudding user response:

The reason for this is the way Tailwind works. It essentially generates necessary CSS classes JIT (just-in-time) based on static analysis of the source files. This means that it will only detect hardcoded string classes, and not classes generated dynamically. When you do grid-cols-${NUM_ROWS_COLS.toString()} it will not see the grid-cols-4 class in the source code, so it will not compile it and there will not be any styles associated with it.

Tailwind does not recommend building dynamic classes for this exact reason (see https://tailwindcss.com/docs/content-configuration#dynamic-class-names). There really isn't any workaround (if you want to keep using Tailwind) except having all the necessary CSS classes in the code somewhere.

CodePudding user response:

You can set style like this below. This work for me.

className={`grid-rows-${N_ROWS_COLS.toString()}`}
  • Related