Can't fix carousel algorithm in react - javascript

I have a carousel of slides in my react.js project without using any libraries. When I use an odd amount of images everything works. But when I use even amount, although currentIndex is changing properly only odd images are displayed like 1,3,5 in this example with six images. Can anyone spot what is wrong with my code so it would work with ane amount of images not only with odd ones? Thanks very much
import React from 'react';
import Slide from './Slide';
import img1 from "../assets/img1.jpg";
import img2 from "../assets/img2.jpg";
import img3 from "../assets/img3.jpg";
import img4 from "../assets/img4.jpg";
import img5 from "../assets/img5.jpg";
import img6 from "../assets/img6.jpg";
class Test extends React.Component {
state = {
currentIndex: 0,
images: [img1, img2, img3, img4, img5, img6]
}
prevSlide = () => {
const lastIndex = this.state.images.length - 1;
const resetIndex = this.state.currentIndex === 0;
const index = resetIndex ? lastIndex : this.state.currentIndex - 1;
this.setState({
currentIndex: index
});
};
nextSlide = () => {
const lastIndex = this.state.images.length - 1;
const resetIndex = this.state.currentIndex === lastIndex;
const index = resetIndex ? 0 : this.state.currentIndex + 1;
this.setState({
currentIndex: index
});
};
render() {
const index = this.state.currentIndex;
let newImagesArray = this.state.images.slice(index, index + 6);
if (newImagesArray.length < 6) {
newImagesArray = newImagesArray.concat(
this.state.images.slice(0, 6 - newImagesArray.length)
);
}
return (
<div className="paint__container">
{newImagesArray.map((image, i) =>
this.state.currentIndex === i ? (
<Slide key={i} url={image} alt="" />
) : null
)}
<div className="left__arrow" onClick={this.prevSlide}></div>
<div className="right__arrow" onClick={this.nextSlide}></div>
</div>
);
}
}
export default Test;

okay, thank you for providing the full code, looking at the component on github
we can find
you have nextSlide defined twice, where the second I guess will overwrite the first declaration
while you have the currentIndex in state why you are searching for the target slide in your render function? you don't have to do this my friend, while currentIndex correctly calculate the index then you just render the slide at that index, that's why we are using react after all
render() {
const index = this.state.currentIndex;
const images = this.state.images;
return (
<div className="paint__container">
<Slide url={images[index]} alt="" />
<div className="left__arrow" onClick={this.prevSlide}></div>
<div className="right__arrow" onClick={this.nextSlide}></div>
</div>
);
}

Related

How to use the UseState hook on an array of objects in react

I have a 10*10 grid, each cell having its own unique id/index. I loop over them and render the grid on the page. then I calculate the coordinates corresponding to each cell. What I'm trying to do next, is add an event listener so whenever an individual cell gets clicked, its background color changes to green (according to its coordinates which we calculated before); however, the background color does not seem change on click:
screenshot
import { useEffect, useState } from "react";
import "./styles.css";
export default function App() {
// dynamically set the grid size
const [grid] = useState(10);
// here's my problem, I can't figure out how to change the background color
// of a cell using the UseState hook
const [isGreen] = useState({});
// get the x, y coordinates
const getCoordinates = (id) => {
const y =
id % grid !== 0 ? Math.floor(id / grid) : Math.floor((id - 1) / grid);
const x = id % grid !== 0 ? id - Math.floor(id / grid) * grid : grid;
return {
x: x,
y: y + 1
};
};
// make them readable e.g: {x: 1, y: 2} -> (1, 2)
const readableCoordinates = (obj) => {
return `(${obj.x}, ${obj.y})`;
};
// not sure if this is the right way to do it
const makeItGreen = (x, y) => {
isGreen[`(${x}, ${y})`] = true;
};
return (
<div className="App">
<h1>My Coordinates</h1>
<div className="grid">
{[...Array(100)].map((e, i) => {
return (
<div
key={i}
style={
isGreen[
`(${getCoordinates(i + 1).x}, ${getCoordinates(i + 1).y})`
] && {
backgroundColor: 'green'
}
}
onClick={() => {
makeItGreen(
getCoordinates(i + 1).x,
getCoordinates(i + 1).y
);
}}
>
{readableCoordinates(getCoordinates(i + 1))}
</div>
);
})}
</div>
</div>
);
}
please read a bit more about useState usage in docs. It seems like you are using it wrong

REACT Cannot find module for images

Trying to load images into react, this is just a test eventually I'll have it all in an array thats saved as Data for switching about. However I can't get the image to load I constantly get cannot find module, I've got url and file loader installed, I've tried placing the images folder in various locations, its currently in src. I can get the same image to load from my CSS file for a background.
import React, { useState } from 'react';
import Scoreboard from './Scoreboard';
import './CSS/Game.css';
import Data from './Data';
import geeAtherton from '../Images/gee-atherton.png';
const Game = (props) => {
const riders = Data;
const [score, setScore] = useState(0);
const [highScore, setHighScore] = useState(0);
const [cardOne, setCardOne] = useState(riders[0]);
const [cardTwo, setCardTwo] = useState(riders[1]);
const [cardThree, setCardThree] = useState(riders[2]);
/* Plays on click and checks answer
NEED TO REQUIRE SCORE UPDATE
*/
function playRound(choice){
console.log(choice)
if (!riders[choice].used){
setScore(score +1);
riders[choice].used = true
}else{
alert('loser')
newGame();
}
if (score === 4){
alert('Jeez Louise you win!')
newGame();
}
else {
setCards();
}
}
/*checks to ensure at least one card hasn't been picked
and then sets the new card values*/
function setCards(){
shuffle(riders)
if (riders[0].used === true && riders[1].used === true && riders[2].used === true){
setCards();
}
else {
setCardOne(riders[0]);
setCardTwo(riders[1]);
setCardThree(riders[2]);
}
}
/* Resets the game*/
function newGame(){
for(let i = 0; i < riders.length; i++){
if (riders[i].used === true){
riders[i].used = false;
}
}
if (score > highScore){
setHighScore(score);
}
setScore(0);
setCards();
}
/* Shuffle the array */
function shuffle(array){
var currentIndex = array.length, temporaryValue, randomIndex;
while (0 !== currentIndex) {
randomIndex = Math.floor(Math.random() * currentIndex);
currentIndex -= 1;
temporaryValue = array[currentIndex];
array[currentIndex] = array[randomIndex];
array[randomIndex] = temporaryValue;
}
}
return (
<div>
<Scoreboard score = {score}
highScore = {highScore}/>
<div id = "game-container">
<div className = "card" onClick = {() => playRound(0)}>
<img src = {require(geeAtherton)} alt ={cardOne.name}></img></div>
<div className = "card" onClick = {() => playRound(1)}>{cardTwo.id}</div>
<div className = "card" onClick = {() => playRound(2)}>{cardThree.id}</div>
</div>
</div>
)
}
export default Game
You don't need to use require in img tag.
<img src = {geeAtherton} alt ={cardOne.name}></img>
I would create in my public file a folder called images. public/images I would save gee-atherton.png in there public/images/gee-atherton.png and then call it by its url.
<img src='/images/gee-atherton.png' alt={cardOne.name}></img>

React Hooks not changing object state until I scroll the page

I'm new with React, and I'm having a problem rendering a component ("setBoard" don't take effect until I scroll the page):
function GameManager() {
const [board, setBoard] = useState(new Board)
const handleKeyDown = useCallback(event => {
if (board.hasWon()) {
return;
}
if (event.keyCode >= 37 && event.keyCode <= 40) {
event.preventDefault();
var direction = event.keyCode - 37;
setBoard(board.move(direction))
}
}, [])
useEffect(() => {
window.addEventListener('keydown', handleKeyDown)
return() => {window.removeEventListener('keydown', handleKeyDown)}
}, [handleKeyDown])
return (
// <div onTouchStart={this.handleTouchStart.bind(this)} onTouchEnd={this.handleTouchEnd.bind(this)}>
<div>
<div class="heading" >
<button class="restart-button" onClick={restartGame} onTouchEnd={restartGame}>New Game</button>
< Score score={board.score} />
</div>
<BoardView board={board} />
</div>
);
}
My component was not re rendering, but the values of object "board" were changing:
return( </div> <BoardView board={board} /> </div>)
After some research, I found that React compares if the object has changed with Object.is, and the response is true, maybe because how I coded "board":
Board.prototype.move = function (direction) {
// 0 -> left, 1 -> up, 2 -> right, 3 -> down
this.clearOldTiles();
for (var i = 0; i < direction; ++i) {
this.cells = rotateLeft(this.cells);
}
var hasChanged = this.moveLeft();
for (var i = direction; i < 4; ++i) {
this.cells = rotateLeft(this.cells);
}
if (hasChanged) {
this.addRandomTile();
}
this.setPositions();
return this;
};
Maybe I should return something different than "return this" in .move()??
Any ideas??? (:
Thanks for reading!
EDIT:
Now is working, but I'm not sure if it was the best approach:
Changed the props in the component, instead of all the object:
<BoardView boardTiles={board.tiles} boardCells={board.cells} />
Now, it should update when board.tiles or board.cells changes.
Also change the way I set the object state (vars separately)
board.move(direction)
setBoard(prevBoard => ({...prevBoard, tiles: board.tiles, cells: board.cells, score: board.score}))
Is this way efficient? can be optimized?
Thanks!

How to correctly wait on state to update/render instead of using a delay/timeout function?

I will attempt to keep this brief, but I am not 100% sure of the correct method of achieving what I am aiming for. I have been thrown in the deep end with React with not much training, so I have most likely been going about most of this component incorrectly, a point in the right direction will definitely help, I don't really expect for someone to completely redo my component for me as it's quite long.
I have a navigation bar SubNav, that finds the currently active item based upon the url/path, this will then move an underline element that inherits the width of the active element. To do this, I find the position of the active item and position accordingly. The same goes for when a user hovers over another navigation item, or when the window resizes it adjusts the position accordingly.
I also have it when at lower resolutions, when the nav gets cut off to have arrows appear to scroll left/right on the navigation to view all navigation items.
Also, if on a lower resolution and the currently active navigation item is off screen, the navigation will scroll to that item and then position the underline correctly.
This, currently works as I have it in my component, this issue is, I don't believe I have done this correctly, I am using a lodash function delay to delay at certain points (I guess to get the correct position of certain navigation items, as it isn't correct at the time of the functions call), which I feel is not the way to go. This is all based on how fast the page loads etc and will not be the same for each user.
_.delay(
() => {
setSizes(getSizes()),
updateRightArrow(findItemInView(elsRef.length - 1)),
updateLeftArrow(findItemInView(0));
},
400,
setArrowStyle(styling)
);
Without using the delay, the values coming back from my state are incorrect as they haven't been set yet.
My question is, how do I go about this correctly? I know my code below is a bit of a read but I have provided a CODESANBOX to play about with.
I have 3 main functions, that all sort of rely on one another:
getPostion()
This function finds the active navigation item, checks if it's within the viewport, if it is not, then it changes the left position of the navigation so it's the leftmost navigation item on the screen, and via setSizes(getSizes()) moves the underline directly underneath.
getSizes()
This is called as an argument within setSizes to update the sizes state, which returns the left and right boundaries of all navigation items
getUnderlineStyle()
This is called as an argument within setUnderLineStyle within the getSizes() function to update the position of the underline object in relation to the position of active navigation item grabbed from the sizes state, but I have to pass the sizesObj as an argument in setSizes as the state has not been set. I think this is where my confusion began, I think I was under the impression, that when I set the state, I could then access it. So, I started using delay to combat.
Below is my whole Component, but can be seen working in CODESANBOX
import React, { useEffect, useState, useRef } from "react";
import _ from "lodash";
import { Link, Route } from "react-router-dom";
import "../../scss/partials/_subnav.scss";
const SubNav = props => {
const subNavLinks = [
{
section: "Link One",
path: "link1"
},
{
section: "Link Two",
path: "link2"
},
{
section: "Link Three",
path: "link3"
},
{
section: "Link Four",
path: "link4"
},
{
section: "Link Five",
path: "link5"
},
{
section: "Link Six",
path: "link6"
},
{
section: "Link Seven",
path: "link7"
},
{
section: "Link Eight",
path: "link8"
}
];
const currentPath =
props.location.pathname === "/"
? "link1"
: props.location.pathname.replace(/\//g, "");
const [useArrows, setUseArrows] = useState(false);
const [rightArrow, updateRightArrow] = useState(false);
const [leftArrow, updateLeftArrow] = useState(false);
const [sizes, setSizes] = useState({});
const [underLineStyle, setUnderLineStyle] = useState({});
const [arrowStyle, setArrowStyle] = useState({});
const [activePath, setActivePath] = useState(currentPath);
const subNavRef = useRef("");
const subNavListRef = useRef("");
const arrowRightRef = useRef("");
const arrowLeftRef = useRef("");
let elsRef = Array.from({ length: subNavLinks.length }, () => useRef(null));
useEffect(
() => {
const reposition = getPosition();
subNavArrows(window.innerWidth);
if (!reposition) {
setSizes(getSizes());
}
window.addEventListener(
"resize",
_.debounce(() => subNavArrows(window.innerWidth))
);
window.addEventListener("resize", () => setSizes(getSizes()));
},
[props]
);
const getPosition = () => {
const activeItem = findActiveItem();
const itemHidden = findItemInView(activeItem);
if (itemHidden) {
const activeItemBounds = elsRef[
activeItem
].current.getBoundingClientRect();
const currentPos = subNavListRef.current.getBoundingClientRect().left;
const arrowWidth =
arrowLeftRef.current !== "" && arrowLeftRef.current !== null
? arrowLeftRef.current.getBoundingClientRect().width
: arrowRightRef.current !== "" && arrowRightRef.current !== null
? arrowRightRef.current.getBoundingClientRect().width
: 30;
const activeItemPos =
activeItemBounds.left * -1 + arrowWidth + currentPos;
const styling = {
left: `${activeItemPos}px`
};
_.delay(
() => {
setSizes(getSizes()),
updateRightArrow(findItemInView(elsRef.length - 1)),
updateLeftArrow(findItemInView(0));
},
400,
setArrowStyle(styling)
);
return true;
}
return false;
};
const findActiveItem = () => {
let activeItem;
subNavLinks.map((i, index) => {
const pathname = i.path;
if (pathname === currentPath) {
activeItem = index;
return true;
}
return false;
});
return activeItem;
};
const getSizes = () => {
const rootBounds = subNavRef.current.getBoundingClientRect();
const sizesObj = {};
Object.keys(elsRef).forEach(key => {
const item = subNavLinks[key].path;
const el = elsRef[key];
const bounds = el.current.getBoundingClientRect();
const left = bounds.left - rootBounds.left;
const right = rootBounds.right - bounds.right;
sizesObj[item] = { left, right };
});
setUnderLineStyle(getUnderlineStyle(sizesObj));
return sizesObj;
};
const getUnderlineStyle = (sizesObj, active) => {
sizesObj = sizesObj.length === 0 ? sizes : sizesObj;
active = active ? active : currentPath;
if (active == null || Object.keys(sizesObj).length === 0) {
return { left: "0", right: "100%" };
}
const size = sizesObj[active];
const styling = {
left: `${size.left}px`,
right: `${size.right}px`,
transition: `left 300ms, right 300ms`
};
return styling;
};
const subNavArrows = windowWidth => {
let totalSize = sizeOfList();
_.delay(
() => {
updateRightArrow(findItemInView(elsRef.length - 1)),
updateLeftArrow(findItemInView(0));
},
300,
setUseArrows(totalSize > windowWidth)
);
};
const sizeOfList = () => {
let totalSize = 0;
Object.keys(elsRef).forEach(key => {
const el = elsRef[key];
const bounds = el.current.getBoundingClientRect();
const width = bounds.width;
totalSize = totalSize + width;
});
return totalSize;
};
const onHover = active => {
setUnderLineStyle(getUnderlineStyle(sizes, active));
setActivePath(active);
};
const onHoverEnd = () => {
setUnderLineStyle(getUnderlineStyle(sizes, currentPath));
setActivePath(currentPath);
};
const scrollRight = () => {
const currentPos = subNavListRef.current.getBoundingClientRect().left;
const arrowWidth = arrowRightRef.current.getBoundingClientRect().width;
const subNavOffsetWidth = subNavRef.current.clientWidth;
let nextElPos;
for (let i = 0; i < elsRef.length; i++) {
const bounds = elsRef[i].current.getBoundingClientRect();
if (bounds.right > subNavOffsetWidth) {
nextElPos = bounds.left * -1 + arrowWidth + currentPos;
break;
}
}
const styling = {
left: `${nextElPos}px`
};
_.delay(
() => {
setSizes(getSizes()),
updateRightArrow(findItemInView(elsRef.length - 1)),
updateLeftArrow(findItemInView(0));
},
500,
setArrowStyle(styling)
);
};
const scrollLeft = () => {
const windowWidth = window.innerWidth;
// const lastItemInView = findLastItemInView();
const firstItemInView = findFirstItemInView();
let totalWidth = 0;
const hiddenEls = elsRef
.slice(0)
.reverse()
.filter((el, index) => {
const actualPos = elsRef.length - 1 - index;
if (actualPos >= firstItemInView) return false;
const elWidth = el.current.getBoundingClientRect().width;
const combinedWidth = elWidth + totalWidth;
if (combinedWidth > windowWidth) return false;
totalWidth = combinedWidth;
return true;
});
const targetEl = hiddenEls[hiddenEls.length - 1];
const currentPos = subNavListRef.current.getBoundingClientRect().left;
const arrowWidth = arrowLeftRef.current.getBoundingClientRect().width;
const isFirstEl =
targetEl.current.getBoundingClientRect().left * -1 + currentPos === 0;
const targetElPos = isFirstEl
? targetEl.current.getBoundingClientRect().left * -1 + currentPos
: targetEl.current.getBoundingClientRect().left * -1 +
arrowWidth +
currentPos;
const styling = {
left: `${targetElPos}px`
};
_.delay(
() => {
setSizes(getSizes()),
updateRightArrow(findItemInView(elsRef.length - 1)),
updateLeftArrow(findItemInView(0));
},
500,
setArrowStyle(styling)
);
};
const findItemInView = pos => {
const rect = elsRef[pos].current.getBoundingClientRect();
return !(
rect.top >= 0 &&
rect.left >= 0 &&
rect.bottom <= window.innerHeight &&
rect.right <= window.innerWidth
);
};
const findLastItemInView = () => {
let lastItem;
for (let i = 0; i < elsRef.length; i++) {
const isInView = !findItemInView(i);
if (isInView) {
lastItem = i;
}
}
return lastItem;
};
const findFirstItemInView = () => {
let firstItemInView;
for (let i = 0; i < elsRef.length; i++) {
const isInView = !findItemInView(i);
if (isInView) {
firstItemInView = i;
break;
}
}
return firstItemInView;
};
return (
<div
className={"SubNav" + (useArrows ? " SubNav--scroll" : "")}
ref={subNavRef}
>
<div className="SubNav-content">
<div className="SubNav-menu">
<nav className="SubNav-nav" role="navigation">
<ul ref={subNavListRef} style={arrowStyle}>
{subNavLinks.map((el, i) => (
<Route
key={i}
path="/:section?"
render={() => (
<li
ref={elsRef[i]}
onMouseEnter={() => onHover(el.path)}
onMouseLeave={() => onHoverEnd()}
>
<Link
className={
activePath === el.path
? "SubNav-item SubNav-itemActive"
: "SubNav-item"
}
to={"/" + el.path}
>
{el.section}
</Link>
</li>
)}
/>
))}
</ul>
</nav>
</div>
<div
key={"SubNav-underline"}
className="SubNav-underline"
style={underLineStyle}
/>
</div>
{leftArrow ? (
<div
className="SubNav-arrowLeft"
ref={arrowLeftRef}
onClick={scrollLeft}
/>
) : null}
{rightArrow ? (
<div
className="SubNav-arrowRight"
ref={arrowRightRef}
onClick={scrollRight}
/>
) : null}
</div>
);
};
export default SubNav;
You can make use of useLayoutEffect hook to determine whether the values have been updated and take an action. Since you want to determine whether all the values has been updated, you need to compare old and new values in useEffect. You can refer to the below post to know how to write a usePrevious custom hook
How to compare oldValues and newValues on React Hooks useEffect?
const oldData = usePrevious({ rightArrow, leftArrow, sizes});
useLayoutEffect(() => {
const {rightArrow: oldRightArrow, leftArrow: oldLeftArrow, sizes: oldSizes } = oldData;
if(oldRightArrow !== rightArrow && oldLeftArrow !== leftArrow and oldSizes !== sizes) {
setArrowStyle(styling)
}
}, [rightArrow, leftArrow, sizes])
I think the reason of your delay is necessary here since you calculate based on rectangles of the first and the last element which are affected when you click on button and do animation of scrolling 500ms. So as a result your calculation needs to wait for animation to be done. change the number of animation and delay you will see the relation.
the style I meant.
#include transition(all 500ms ease);
In short, I think what you are using is the right way as long as you have animations related to the calculation.
setState takes an optional second argument which is a callback that executes after the state has been updated and the component has been re-rendered.
Another option is the componentDidUpdate lifecycle method.

Warning: setState(...): Can only update a mounted or mounting component. This usually means

I am working on an exercise in the Udemy Advanced Webdeveloper Bootcamp. The exercise asked to come up with a page of 32 boxes that randomly change colour (every x seconds). My solution is not exactly that. I change the color of all 32 boxes at the same time. It almost works. I get random 32 boxes initially, but does not change the color later. My console tells me I am doing something wrong with the setState. But I cannot figure out what. I think my changeColor is a pure function:
import React, { Component } from 'react';
import './App.css';
class Box extends Component {
render() {
var divStyle = {
backgroundColor: this.props.color
}
return(
<div className="box" style={divStyle}></div>
);
}
}
class BoxRow extends Component {
render() {
const numOfBoxesInRow = 8;
const boxes = [];
for(var i=0; i < numOfBoxesInRow; i++) {
boxes.push(<Box color={this.props.colors[i]} key={i+1}/>);
}
return(
<div className="boxesWrapper">
{boxes}
</div>
);
}
}
class BoxTable extends Component {
constructor(props) {
super(props);
this.getRandom = this.getRandom.bind(this);
this.changeColors = this.changeColors.bind(this);
this.state = {
randomColors: this.getRandom(this.props.allColors, 32) // hardcoding
};
this.changeColors();
}
changeColors() {
setInterval(
this.setState({randomColors: this.getRandom(this.props.allColors, 32)}), 5000);
}
getRandom(arr, n) {
var result = new Array(n),
len = arr.length,
taken = new Array(len);
if (n > len)
throw new RangeError("getRandom: more elements taken than available");
while (n--) {
var x = Math.floor(Math.random() * len);
result[n] = arr[x in taken ? taken[x] : x];
taken[x] = --len in taken ? taken[len] : len;
}
return result;
}
render () {
const numOfRows = 4;
const rows = [];
for(let i=0; i < numOfRows; i++) {
rows.push(
<BoxRow colors={this.state.randomColors.slice(8*i,8*(1+i))} key={i+1}/>
)
}
return (
<div className="rowsWrapper">
{rows}
</div>
);
}
}
BoxTable.defaultProps = {
allColors: ["AliceBlue","AntiqueWhite","Aqua","Aquamarine","Azure","Beige",
"Bisque","Black","BlanchedAlmond","Blue","BlueViolet","Brown","BurlyWood",
"CadetBlue","Chartreuse","Chocolate","Coral","CornflowerBlue","Cornsilk",
"Crimson","Cyan","DarkBlue","DarkCyan","DarkGoldenRod","DarkGray","DarkGrey",
"DarkGreen","DarkKhaki","DarkMagenta","DarkOliveGreen","Darkorange",
"DarkOrchid","DarkRed","DarkSalmon","DarkSeaGreen","DarkSlateBlue",
"DarkSlateGray","DarkSlateGrey","DarkTurquoise","DarkViolet","DeepPink",
"DeepSkyBlue","DimGray","DimGrey","DodgerBlue","FireBrick","FloralWhite",
"ForestGreen","Fuchsia","Gainsboro","GhostWhite","Gold","GoldenRod","Gray",
"Grey","Green","GreenYellow","HoneyDew","HotPink","IndianRed","Indigo",
"Ivory","Khaki","Lavender","LavenderBlush","LawnGreen","LemonChiffon",
"LightBlue","LightCoral","LightCyan","LightGoldenRodYellow","LightGray",
"LightGrey","LightGreen","LightPink","LightSalmon","LightSeaGreen",
"LightSkyBlue","LightSlateGray","LightSlateGrey","LightSteelBlue",
"LightYellow","Lime","LimeGreen","Linen","Magenta","Maroon",
"MediumAquaMarine","MediumBlue","MediumOrchid","MediumPurple",
"MediumSeaGreen","MediumSlateBlue","MediumSpringGreen","MediumTurquoise",
"MediumVioletRed","MidnightBlue","MintCream","MistyRose","Moccasin",
"NavajoWhite","Navy","OldLace","Olive","OliveDrab","Orange","OrangeRed",
"Orchid","PaleGoldenRod","PaleGreen","PaleTurquoise","PaleVioletRed",
"PapayaWhip","PeachPuff","Peru","Pink","Plum","PowderBlue","Purple",
"Red","RosyBrown","RoyalBlue","SaddleBrown","Salmon","SandyBrown",
"SeaGreen","SeaShell","Sienna","Silver","SkyBlue","SlateBlue","SlateGray",
"SlateGrey","Snow","SpringGreen","SteelBlue","Tan","Teal","Thistle",
"Tomato","Turquoise","Violet","Wheat","White","WhiteSmoke","Yellow","YellowGreen"]
}
export default BoxTable
You are calling this.setState before the component has mounted (from the constructor). Try instead making your first this.ChangeColors from the componentDidMount lifecycle function.
Additionally, it's not a bad idea to clear the interval when it unmounts in componentWillUnMount
Edit: By changing the interval to call after the first wait you do prevent the error, for now. I'd recommend using the lifecycle functions to build good habits. It's just a temporary assignment, but in a full project you'd be putting yourself at risk to break the component again later by making it possible to call this.setState before it is reliably mounted.
You need to use a lambda function in order to use setState inside setInterval
setInterval(() => {
this.setState({randomColors: this.getRandom(this.props.allColors,
32)});
}, 5000)
try to change your changeColors function to like this:
changeColors() {
setInterval(() => this.setState({randomColors: this.getRandom(this.props.allColors, 32)}), 5000);
}
the first param of setInterval is function, in your original code you already executed this setState and didn't passed the function itself
You will need to update the state after the component creation phase, inside componentDidMount()
class BoxTable extends Component {
constructor(props) {
super(props);
this.getRandom = this.getRandom.bind(this);
this.changeColors = this.changeColors.bind(this);
this.state = {
randomColors: this.getRandom(this.props.allColors, 32) // hardcoding
};
// delete this line
//this.changeColors();
}
// replace changeColors by componentDidMount,
// this function will be called automatically by react
componentDidMount() {
setInterval(
this.setState({randomColors: this.getRandom(this.props.allColors, 32)}), 5000);
}
getRandom(arr, n) {
var result = new Array(n),
len = arr.length,
taken = new Array(len);
if (n > len)
throw new RangeError("getRandom: more elements taken than available");
while (n--) {
var x = Math.floor(Math.random() * len);
result[n] = arr[x in taken ? taken[x] : x];
taken[x] = --len in taken ? taken[len] : len;
}
return result;
}
render () {
const numOfRows = 4;
const rows = [];
for(let i=0; i < numOfRows; i++) {
rows.push(
<BoxRow colors={this.state.randomColors.slice(8*i,8*(1+i))} key={i+1}/>
)
}
return (
<div className="rowsWrapper">
{rows}
</div>
);
}
}

Categories

Resources