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()}`}