1

The goal I have is to click on the button and for the button to change color until I click it again. I had the code working with a single button but when I tried to make an array of buttons I ran into trouble, I feel like I misses something obvious but can't find it

//GRID.js
    import React, { useState, useEffect } from "react";

import { Cell } from "./cell";

export const Grid = () => {
  const ARRAYLENGTH = 3;
  const [grid, setGrid] = useState(Array(ARRAYLENGTH).fill({ show: true }));

  useEffect(() => {
    console.log("updated");
  }, [grid]);

  const handlePress = (index) => {
    let tempGrid = grid;
    tempGrid[index].show = !tempGrid[index].show;
    setGrid(tempGrid);
    console.log(grid[index].show);
    logGrid();
  };

  const logGrid = () => {
    console.log(grid);
  };
//Renders cell.js
  return grid.map((item, index) => {
    return (

      <Cell
        key={`${item} ${index}`}
        show={grid[index].show}
        index={index}
        onClick={handlePress}
      />
    );
  });
};

//CELL.JS

import React, { useState, useEffect } from "react";
import styled from "styled-components/native";
import { View, Pressable } from "react-native";
import * as theme from "../../config/theme";
//Styles
const StyledCell = styled(Pressable)`
  padding: 30px;
  border-color: black;
  border-width: 1px;
  background-color: ${(props) => props.color};
`;
Flanmcgan
  • 21
  • 5
  • `.fill({ show: true })` makes aliases of the same object. Try `.fill().map(() => ({show: true}))`. Secondly, don't mutate state like `tempGrid[index].show = ...`. Just pass a new object with the show property set to `setGrid`, otherwise bad things can happen. State setting is async, so `console.log(grid[index].show)` [won't show the new value and is probably confusing you](https://stackoverflow.com/questions/41278385/setstate-doesnt-update-the-state-immediately). – ggorlen May 19 '21 at 03:27
  • 1
    Thanks for taking time out of your day to help me, I uploaded the code as an answer, it works now thanks to your help – Flanmcgan May 19 '21 at 15:26
  • No problem and thanks for sharing your answer as a [self answer](https://stackoverflow.com/help/self-answer) -- it can help future readers. One issue with your new code is `let tempGrid = [...grid];` is a shallow copy, so you're still mutating state on the `tempGrid[index].show = ...` line. It might appear to work, but that's not guaranteed -- you might miss a render or have odd behavior. Your code will probably look something like [this](https://stackoverflow.com/a/65367032/6243352) to adjust the property in the object in an array. – ggorlen May 19 '21 at 15:29

1 Answers1

0

Here is the updated code for anyone who would like to see it

//grid.js
import React, { useState } from "react";

import { Cell } from "./cell";

export const Grid = () => {
  const ARRAYLENGTH = 4;
  const [grid, setGrid] = useState(
    Array(ARRAYLENGTH)
      .fill()
      .map(() => ({ show: true }))
  );

  const handlePress = (index) => {
    let tempGrid = [...grid];
    tempGrid[index].show = !tempGrid[index].show;
    setGrid(tempGrid);
  };

  return grid.map((item, index) => {
    return (
      <Cell
        key={`${item} ${index}`}
        show={item.show}
        index={index}
        onClick={handlePress}
      />
    );
  });
};

//cell.js

import React, { useState, useEffect } from "react";
import styled from "styled-components/native";
import { View, Pressable } from "react-native";
import * as theme from "../../config/theme";
const StyledCell = styled(Pressable)`
  padding: 30px;
  border-color: black;
  border-width: 1px;
  background-color: ${(props) => props.color};
`;
export const Cell = ({ show, onClick, index }) => {
  const [color, setColor] = useState(theme.blue);
  useEffect(() => {
    if (show === true) {
      setColor(theme.blue);
    } else {
      setColor(theme.white);
    }
  }, [show]);
  return (
    <View>
      <StyledCell
        color={color}
        onPress={() => {
          onClick(index);
        }}
      />
    </View>
  );
};
Flanmcgan
  • 21
  • 5