Creé un componente de cuadrícula en React. Tengo una serie de cadenas denominadas 'colores disponibles' en las que almaceno los nombres de clase css que quiero usar.
En el componente 'RandomColorGrid' estoy configurando los colores iniciales de cada elemento de la cuadrícula en 'useState', asignando un índice de 'disponibleColors' a cada elemento.
Cada elemento de la cuadrícula llama a 'cambiar colores ()' al hacer clic. Dentro de ese método, reasigno el valor de cada 'caja' en 'colores' con un nuevo índice elegido al azar de 'colores disponibles'.
Esto funciona lo suficientemente bien, pero se siente un poco torpe. Hay dos cosas que estoy tratando de mejorar pero me estoy atascando.
Primero; Me gustaría usar cada color solo una vez cuando se llama a la función 'changeColors()'. Actualmente es posible que se use el mismo color en más de un elemento de la cuadrícula y me gustaría que fueran cuatro colores únicos cada vez.
Segundo; Me gustaría que ningún artículo sea del mismo color dos veces seguidas. Entonces, para cualquier elemento dado, me alinearía para excluir el color actual de ese elemento de las posibles opciones aleatorias.
He estado tratando de lograr esto tomando el índice de color del color actual y formando una nueva matriz de colores para seleccionar al azar para cada elemento y luego otra matriz para tratar de rastrear los colores que ya se han usado para evitar duplica pero al hacerlo se han metido en un verdadero lío. Esto me lleva a creer que mi diseño probablemente sea malo desde el principio.
¿Cómo puedo mejorar esto?
import React, { useState } from "react"; const availableColors = ["red", "green", "blue", "yellow"]; const changeColors = (colors, setColors) => { colors.box1 = availableColors[randomNumber(colors.box1)]; colors.box2 = availableColors[randomNumber(colors.box2)]; colors.box3 = availableColors[randomNumber(colors.box3)]; colors.box4 = availableColors[randomNumber(colors.box4)]; setColors({ ...colors }); }; const randomNumber = (currentColour) => { let indices = [0, 1, 2, 3]; indices.splice(availableColors.indexOf(currentColour), 1); return indices[Math.floor(Math.random() * indices.length)]; }; export const RandomColorGrid = () => { let [colors, setColors] = useState({ box1: availableColors[0], box2: availableColors[1], box3: availableColors[2], box4: availableColors[3], }); return ( <div className="grid"> <div className={`${colors.box1}`} onClick={() => changeColors(colors, setColors)} /> <div className={`${colors.box2}`} onClick={() => changeColors(colors, setColors)} /> <div className={`${colors.box3}`} onClick={() => changeColors(colors, setColors)} /> <div className={`${colors.box4}`} onClick={() => changeColors(colors, setColors)} /> </div> ); };Tus problemas provienen de no respetar la inmutabilidad de los objetos. Cambia un objeto y confía en que el objeto no cambie en la siguiente línea (en changeColors)
La solución sería copiar nuevas matrices de los colores disponibles y usar .filter para asegurarnos de que no repetimos los mismos colores dos veces reemplazando la nueva matriz currentAvailableColors para incluir solo los colores que se pueden usar.
const changeColors = (colors, setCurrentColours) => { const nextColors = {}; let currentlyAvailableColors = [...availableColors]; nextColors.box1 = getRandomOption(colors.box1, currentlyAvailableColors) currentlyAvailableColors = currentlyAvailableColors.filter(col => col !== nextColors.box1); nextColors.box2 = getRandomOption(colors.box2, currentlyAvailableColors) currentlyAvailableColors = currentlyAvailableColors.filter(col => col !== nextColors.box2); nextColors.box3 = getRandomOption(colors.box3, currentlyAvailableColors) currentlyAvailableColors = currentlyAvailableColors.filter(col => col !== nextColors.box3); nextColors.box4 = getRandomOption(colors.box4, currentlyAvailableColors) currentlyAvailableColors = currentlyAvailableColors.filter(col => col !== nextColors.box4); setCurrentColours({ ...nextColors }); };Aquí hay un codepen que funciona https://codepen.io/yftachman/pen/XWZMqVZ