Jak wygenerować siatkę tylko raz?

Wątek przeniesiony 2024-10-16 16:35 z JavaScript przez Riddle.

0

useState() uruchamia dwa razy ta sama funkcje:

import './App.css'
import GamePlan from './GamePlan'
import { useState } from "react";

function App() {

  function trySt(){
    console.log("tryNormaleeeeeee");
  }
  
  const [tryStatee, setTryStatee] = useState(trySt);

  return (
    <div>
      <GamePlan/>
    </div>
  )
}

export default App

W main.tsx używam strictmode:

import { StrictMode } from 'react'
import { createRoot } from 'react-dom/client'
import App from './App.tsx'
import './index.css'

createRoot(document.getElementById('root')!).render(
  <StrictMode>
    <App />
  </StrictMode>,
)

Jednak w innym projekcie usestate wywoluje funckje tylko raz. Pewnie mam cos namieszane w ustawieniach?

Dokleje package.json:

{
  "name": "candycrushreactts",
  "private": true,
  "version": "0.0.0",
  "type": "module",
  "scripts": {
    "dev": "vite --port 4000",
    "build": "tsc -b && vite build",
    "lint": "eslint .",
    "preview": "vite preview"
  },
  "dependencies": {
    "react": "^18.3.1",
    "react-dom": "^18.3.1"
  },
  "devDependencies": {
    "@eslint/js": "^9.9.0",
    "@types/react": "^18.3.3",
    "@types/react-dom": "^18.3.0",
    "@vitejs/plugin-react": "^4.3.1",
    "eslint": "^9.9.0",
    "eslint-plugin-react-hooks": "^5.1.0-rc.0",
    "eslint-plugin-react-refresh": "^0.4.9",
    "globals": "^15.9.0",
    "typescript": "^5.5.3",
    "typescript-eslint": "^8.0.1",
    "vite": "^5.4.1"
  }
}
1

Panie, próbujesz pan trzymać funkcję w stanie - to raczej nie jest dobra praktyka. Jaki jest cel takiego trzymania? useState wykonuje tą funkcję, bo useState ja dostaje jako initialValue, a initialValue może być też funkcja do wyliczenia stanu inicjalnego. W twoi wypadku traktuje właśnie w taki sposób tą funkcję i ją wykonuje.

0

To tylko przykład, w normalnym kodzie po prostu generuje jakieś elementy w stanie wyjściowym:

const createGrids = ():Array<GridProps> => {
    const arr:Array<GridProps> = [];
    for(let i = 0; i < gridsInRow; i++) {
        for(let j = 0; j < gridsInCol; j++) {
            arr.push({
                color:colorsArr[Math.floor(Math.random() * colorsArr.length)],
                row: i,
                col: j,
                onDragStart: onDragStart,
                onDragDrop: onDragDrop,
                //key: i*9 + j,
            });
        }
    }
    console.log(arr.length, arr);
    return arr;
}

const [gridsArray, setGridsArray] = useState<GridProps[]>(createGrids);

To tez jest raczej ujowa praktyka? Powinienem bardziej zrobic cos w stylu:

const [gridsArray, setGridsArray] = useState<GridProps[]>([]);

function create()
{
  let arr = [];
  blebleble
  
  setGridsArray(() => arr);
}

create();
3

<StrictMode> renderuje komponenty dwa razy, aby wykryć niektóre błędy (https://react.dev/reference/react/StrictMode).

Działa jedynie w trybie deweloperskim, więc jak zbudujesz projekt przez npm run build i odpalisz to wszystko będzie w porządku.

2

Pytanie zasadnicze: Czy ta funkcja którą wołasz, to czysta funkcja (bez żadnych efektów uobczonych), czy jednak coś robi?

  • Jeśli to jest pure-funkcja, to Ci zależy czy się wykona raz czy dwa? Nie ma znaczenia.
  • Natomiast jeśli funkcja ma jakiś efekt uboczny, albo z jakiegoś innego powodu chcesz żeby się wykonała raz, to użyj odpowiedniego hooka do tego, np. useEffect():
    function MyComponent({gridsInRow}) {
      const [grid, setGrid] = useState(null);
    
      useEffect(function () {
        const gridsInRow = {}
        for(let i = 0; i < gridsInRow; i++) { // tutaj sobie wygeneruj grid
        }
    
        setGrid(generatedGrid);
      }, [gridsInRow]);
    
      return <div grid={grid}/>
    }
    
3

Tak jak wyżej wspomniał @Xarviel to udokumentowane i celowe działanie żeby się upewnić że twoje funkcje są pure, nie mają efektów ubocznych i mogą być uruchomione w każdym momencie. Nie powinieneś polegać na tym ile razy funkcja będzie odpalona, ale w trybie produkcyjnym będą wywoływane rzadziej.

0

Zalezy co macie na mysli mowiac o efektach ubocznych :-) Na moj chlopski rozum funkcja generuje po prostu jakas kolorowa siatke i nic wiecej, zdziwilo mnie tylko to, ze funkcja uruchamia sie dwa razy i zaczałem szukac wtf:> Do głowy by mi nie przyszlo ze js moze to generowac 2 razy po prostu w celach szukania błędów.

Swoją drogą jak widzicie funkcja nie genereuje grida a gridpropsy z prostego powodu, grid jest jakims tam divem (czy img pewnie w przyszlosci:

import { useState } from "react";
import Grid from "./Grid";
import { DragGridCalculations } from "./DragGridCalculations";

export const gridsInRow:number = 6;
export const gridsInCol:number = 3;

const colorsArr:Array<string> = ["red", "blue", "green", "yellow", "pink", "purple"];//, "orange"];
let indexStartDrag:number;

const dragCalc:DragGridCalculations = new DragGridCalculations();

export type GridProps = {
    color: string;
    row:number;
    col:number;
    onDragStart: (e:React.DragEvent<HTMLDivElement>) => void;
    onDragDrop: (e:React.DragEvent<HTMLDivElement>) => void;
    //key:number;
    //grid:GridProps;
} 

function GamePlan() {

    const onDragStart = (e:React.DragEvent<HTMLDivElement>) => {
        dragCalc.setGridStart(e);
        const target = e.target as HTMLDivElement;
        console.log(e);
        const row:number = Number(target.dataset.row); const col = Number(target.dataset.col);
        console.log(row + " a w tablicy to chyba : " + (row*gridsInCol + col));
        console.log("drag start " + row); //e.target.attributes.row.value);

        indexStartDrag = row*gridsInCol + col;
        console.log(indexStartDrag);
        e.stopPropagation();
    }

    const onDragDrop = (e:React.DragEvent<HTMLDivElement>) => {
        
        const target = e.target as HTMLDivElement;
        const dropRow:number = Number(target.dataset.row);
        const dropCol:number = Number(target.dataset.col);
        const indexDrop:number = dropRow*gridsInCol + Number(target.dataset.col);

        console.log("index drop: " + indexDrop + " a drop row: " + dropRow + " a drop col: " + dropCol);

        const tempStartDragGridObj:GridProps = gridsArray[indexStartDrag];
        const tempDropGridObj:GridProps = gridsArray[indexDrop];

        if(dragCalc.isAbleToMove(e))
        {
            tempDropGridObj.row = tempStartDragGridObj.row;
            tempDropGridObj.col = tempStartDragGridObj.col;
    
            tempStartDragGridObj.row = dropRow;
            tempStartDragGridObj.col = dropCol;
    
            const tempArr = gridsArray;
    
            tempArr[indexStartDrag] = tempDropGridObj;
            tempArr[indexDrop] = tempStartDragGridObj;

            
    
            //setGridsArray((prev) => tempArr);
            setGridsArray(() => [...tempArr]);

            dragCalc.checkGridsFit(gridsArray, e);
        }
        
    }


    const createGrids = ():Array<GridProps> => {
        const arr:Array<GridProps> = [];
        for(let i = 0; i < gridsInRow; i++) {
            for(let j = 0; j < gridsInCol; j++) {
                arr.push({
                    color:colorsArr[Math.floor(Math.random() * colorsArr.length)],
                    row: i,
                    col: j,
                    onDragStart: onDragStart,
                    onDragDrop: onDragDrop,
                    //key: i*9 + j,
                });
            }
        }
        console.log(arr.length, arr);
        return arr;
    }

    const [gridsArray, setGridsArray] = useState<GridProps[]>(createGrids);



    return (
        <div style={{display: "flex", flexWrap: "wrap", width: 560}}>
        {
            gridsArray.map((grid, index) => {
                return (
                    <Grid key={index} color={grid.color} row={grid.row} col={grid.col} onDragStart={onDragStart} onDragDrop={onDragDrop} />
                );
            })
        }

        </div>
    );
}
export default GamePlan;

import { DragEventHandler, ReactEventHandler } from "react";
import { GridProps } from "./GamePlan";

function Grid(props:GridProps) {

    const row:number = props.row;
    const col:number = props.col;
    const color:string = props.color;

    const onDragEnd = (e) => {
        console.log("drag end", e);
    }

    const onDragOver = (e) => {
        console.log("drag over " + e.clientX, e);
        e.preventDefault();
    }


    return (
        <div className="grid" 
        style={{backgroundColor: props.color}} 
        draggable={true} 
        onDragStart={props.onDragStart} 
        onDrop={props.onDragDrop} 
        onDragEnd={onDragEnd} 
        //onDragEnter={onDragEnter}
        //onDragOver={(e)=> e.preventDefault()}
        onDragOver={onDragOver}
        data-row={row} data-col={col}
        >
        </div>
    );
}

export default Grid;

I pewnie sam Grid w przyszlosci tez bedzie musiał używać jakiegos usestate np w zwiazku z animacja czy to wybuchu czy spadania itd.
Tyle ze to rodzi pewne komplikacje ponieważ normalnie napisałbym Grida jako klase i po prostu tworzył instancję danej klasy. Cos w stylu

for....
let grid = new Grid(color, row, col, blebleble);
arr.push(grid);

Wtedy po kliknieciu w grida dostawalbym w e.target konkretnego grida gedzie bym sobie mial swobodny dostep do wszelkich pól (czy tam zmiennych) jakie bym sobie ustawił w instancji klasy.

Tutaj jednak musze bawic sie w jakies propsy ktore z kolei utrudniaja mi dostep do danych. Musze np. robic cos w stylu:

const target = e.target as HTMLDivElement;
const dropRow:number = Number(target.dataset.row);
const dropCol:number = Number(target.dataset.col);
const indexDrop:number = dropRow*gridsInCol + Number(target.dataset.col);

i te dane z kolei przesyłac tez w jakis dziwny posob:

<div style={{display: "flex", flexWrap: "wrap", width: 560}}>
        {
            gridsArray.map((grid, index) => {
                return (
                    <Grid key={index} color={grid.color} row={grid.row} col={grid.col} onDragStart={onDragStart} onDragDrop={onDragDrop} />
                );
            })
        }

        </div>

Co rozwala mi łeb ponieważ cały czas musze mieć z tyłu głowy ze 1 taki prosty element jak Grid czyli zwykły kolorowy kwadrat w który mozna klikać ma jakieś tak naprawdę 2 warstwy, warstwę props ze wszystkimi danymi i warstwę przeznaczoną do wyswietlenia w formie komponentu. Nie wydaje to się wam dziwactwem?

0
goku21 napisał(a):

Do głowy by mi nie przyszlo ze js moze to generowac 2 razy po prostu w celach szukania błędów.

Nie JS, tylko React.

goku21 napisał(a):

Zalezy co macie na mysli mowiac o efektach ubocznych :-) Na moj chlopski rozum funkcja generuje po prostu jakas kolorowa siatke i nic wiecej, zdziwilo mnie tylko to, ze funkcja uruchamia sie dwa razy i zaczałem szukac wtf:>

Funkcja uruchomi się tyle razy, ile komponent zostanie wyrenderowana. Po prostu użyj useEffect().

Poczytaj tutaj: https://react.dev/reference/react/hooks#effect-hooks

Zarejestruj się i dołącz do największej społeczności programistów w Polsce.

Otrzymaj wsparcie, dziel się wiedzą i rozwijaj swoje umiejętności z najlepszymi.