2D array with size from props only updates one dimension - reactjs

I am working through the Flavio Copes Pixel Art app in his React course and I'd like to add a button that can resize my "canvas". The canvas is a 2D array that is initialized like so:
const [matrix, setMatrix] = useState(
Array(size)
.fill()
.map(() => Array(size).fill(0)));
The size variable is passed down as a prop from the parent component, which has a hook to manage the state from an input field. The matrix is used to render "Pixel" components (30px x 30px divs) by mapping through each row/col like so:
<div className={css(styles.canvas)}>
{matrix.map((row, rowIndex) =>
row.map((_, colIndex) => {
return (
<Pixel
key={`${rowIndex}-${colIndex}`}
background={Colors[matrix[rowIndex][colIndex]]}
onClick={() => changeColor(rowIndex, colIndex)}
/>
);
})
)}
</div>
I am able to update the width of the canvas (# of columns), but I think that the number of total Pixels remain constant and subsequent rows are added/subtracted that do not equal the number of columns. You can replicate the error by inputting a new value into "Canvas Size" and clicking "Resize Canvas" on my demo.
Why do the number of rows differ from the number of columns?
All of the code is available in my repo.

I do not see change of columns/rows in your repo. This function should do the trick
const updateMatrixSize = (size) => {
setMatrix(
matrix.length > size
? matrix.slice(0, size).map(it => it.slice(0, size)))
: [
...matrix.map(it => ([
...it,
...new Array(size - matrix.length).fill(0)
])
),
...new Array(size - matrix.length).fill(0)
]
)
}
PS you have a typo in your repo
props.setMsatrixState(newMatrix);

Related

Updating array state in react

if I have a bunch of boxes and each box has 'data-index'
<div
ref={ref}
className={isClicked ? "box clicked" : " box"}
data-index={img.num}
onClick={BoxClicked}
>
and if I clicked to one of them I want to get the data-index for that box and store it in array of state
so If I clicked to two boxes and box one has data-index=2, and box two has data-index=4
the expected output will be array of these value [2,4]
I try this but for some reasons it didn't work
what happen is when I clicked to another box the previous value is gone and the array is updated to newest data-index's box
const [index, setIndex] = useState([]);
const BoxClicked = () => {
setIsClicked(true);
setIndex((pre) => [...pre, ref.current.dataset.index]);
// setIndex([...index, ref.current.dataset.index]);
};

How can I map an array to heatmap in react using react-heatmap-grid?

I am new to coding and decided to use react-heatmap-grid for heatmap in a web application. I have tried numerous ways and searched the internet but I didn't find an answer. I have 7 rows and 24 columns for days and hours (which makes 168 cells) and im trying to get the data in correctly. Right now it maps the whole array in each cell but I need every element in array in different cell. Currently the mapping of data looks like this:
const testArr = [];
for (var i = 1; i<169; i++){
testArr.push(i);
}
const data = new Array(yLabels.length)
.fill(0)
.map(() =>
new Array(xLabels.length)
.fill(0)
.map(() => TestArr)
)
And the heatmap is created like this:
<div className='heatmap'>
<HeatMap
xLabels={xLabels}
yLabels={yLabels}
yLabelWidth={150}
xLabelsLocation={"top"}
xLabelsVisibility={xLabelsVisibility}
data={data}
cellStyle={(background, value, min, max, data, x, y) => ({
background: `rgb(127,255,0, ${1 - (max - value) / (max - min)})`,
fontSize: "12px",
fontFamily: "Arial",
color: `rgb(30,0,0)`,
})}
cellRender={value => value && `${value}`}
/>
</div>
How can I insert the data in cells so that they all wouldn't have the array in them?
I ditched the mapping and found another solution. Instead of mapping the data, I just use an array of arrays and send it trough to heatmap as data.
<div className='heatmap'>
<HeatMap
xLabels={xLabels}
yLabels={yLabels}
yLabelWidth={150}
xLabelsLocation={"top"}
xLabelsVisibility={xLabelsVisibility}
data={arrayOfArrays}
...

Get access to an array in an array from REST API

Hi I'm trying to display variants in two separate categories Color and Size, to do this I need to get data from the api, I can access "attributes", but I would like to be able to access 0 and 1 and map them, I have no idea how to do this.
{variants.length > 1 ? (
variants.attributes.map(({ name, values }) => (
<ProductOptions
key={`key-${name}`}
name={name}
values={values}
selectedOptions={selectedOptions}
setOptions={setOptions}
/>
))
) : (
<Fragment />
)}
Thank you so much!
As i understand the output of Array with 6 elements where each of that 6 has attributes and attributes is yet another array and you want to loop through those attributes so you need 2 loops. One to loop through parent array and seconds inside the child.
variants.map((variant) => {
variant.attributes.map((attribute) => {
console.log('attribute: ', attribute);
console.log('attribute id: ',attribute.id);
});
});
p.s. you may use forEach https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/forEach but it has little difference in this case.
Also it seems like you working with ReactJS and building some JSX to put into rendered jsx. I would argue to form the array upfront and in final jsx just insert the rows, so your final jsx will be more readable, especially in cases of double loops or any more complex jsx.
const attributes = [];
variants.map((variant) => {
variant.attributes.map((attribute) => {
console.log('attribute: ', attribute);
console.log('attribute id: ',attribute.id);
attributes.push(<div key={attribute.id}>{attribute.id}</div>)
});
});
// later
return (
<div>
{attributes}
</div>
)

Buggy movement of Object in Array with Redux and React

Basically I'm trying to move a single object within an Array of object, but when I move the same object once or twice it starts moving the other object in said array.
So I have tried making a new Array with .slice() then .shift(item) by it's index and then add it back in at the right index with .splice(newIndex, 0, item), once the array has been update I push it to the Redux store which updates my Megadraft(ie Draft.js) application.
I have also tried directly manipulating the original array, ie this.props.array (like your meant too with Redux) and using the keys inside of the objects instead of the indexes.
import React from 'react';
import { MegadraftPlugin, DraftJS, CommonBlock } from "megadraft"
export default class ImageGalleryBlock extends React.Component {
_moveImgOneBack = (e, images, index) =>{
e.preventDefault()
let newPlace = index - 1
if(newPlace == -1){
newPlace = images.length
}
const image = images.shift(index)
images.splice(newPlace, 0, image)
return this.props.container.updateData({ images: images })
}
_moveImgOneForward = (e, images, index) =>{
e.preventDefault()
let newPlace = index +1
if(newPlace > images.length){
newPlace = 0
}
const image = images.shift(index)
images.splice(newPlace, 0, image)
return this.props.container.updateData({ images: images })
}
render(){
return (
<CommonBlock {...this.props} actions={this.actions} title="Image
Gallery">
<BlockContent>
<div className='gallery-cms-block'>
{ this.props.images.map((obj, index)=> {
return(
<div key={obj.key} className="image-box">
<button title="Move image back one" className="move-button"
onClick={(e)=> this._moveImgOneBack(e,
this.props.data.images, index)}>◀ {index}</button>
<img className="image" src={`${obj.image.uri}?
id=${obj.image.id}`} />
<div>
<button key={obj.key} title="Move image forward one"
className="move-button" onClick={(e)=>
this._moveImgOneForward(e, this.props.data.images,
index)}>▶</button>
</div>
</div>
)
}) }
</div>
</BlockContent>
</CommonBlockMKII>
);
}
}
I expect the the button(ether forward or backward) to move said item and only the said item.
The results are that it will move the item once... maybe twice then get suck moving all the other items in the array.
... your using shift wrong:
array = ['foo', 'bar', 'not', 'feeling', 'welcome', 'by jack', 'ass users']
array.shift(whatEverIndex)
the output will always be the first index, i.e 'foo', and
because your index is correct and your using
array.splice(newIndex, 0, item)
properly your array is changing in a strange fashion.
Try copying the desired item then delete it with .splice(), like this:
const item = array[index] //copy item
array.splice(index, 1) //remove old item
array.splice(newIndex, 0, item) //place item
funny that none of you guys NaN, laruiss, Antoine Grandchamp, J-Alex took the time to actually do what you should on stackoverflow... you know help someone out. damn vete a cascarla, Good luck Reece hope this solved it for you.
Thanks #Whitepaw,
I've updated my code with:
_moveOneImgBack = (newArray, index) =>{
const arrayLength = newArray.length - 1
const newBackPlace = index == 0 ? arrayLength : index - 1
const image = newArray[index]
newArray.splice(index, 1)
// const image = images.shift(index)
newArray.splice(newBackPlace, 0, image)
this.props.container.updateData({ images: newArray })
}
and it now works perfectly, I got stuck on the fact it might have something to do with redux immutables. So thats for pointing out the misuse of .shift()

Conditional classes on single instances of component within a loop

I have a loop of components that make a grid and I want some instances to have a background color change triggered by adding a class.
This question gave me ideas but I've yet to get them to work.
I have this basic markup in it.
<div className={`box ${(this.state.backgroundColor ? 'backgroundColor' : null)}`} key={i}>
</div>
And I have an array of the indexes representing the component instances that I want the color changed on
let indexes = [101, 178, 232, 545]
Currently I am doing it with plain JS, via document.querySelector('.box:nth-of-type(101'), but as this method bypasses React I want to change it.
Based on the other question I tried making an array of all the components and setting that to state, then looping through and using the indexes. I don't know how to "access" the component like this and get $$typeof: Symbol(react.element) etc
let compArr = []
this.arr.map((i) => {
compArr.push(
<div className={`box ${(this.state.backgroundColor ? 'backgroundColor' : null)}`} key={i}>
</div>
)
})
this.setState({
compArr: compArr
})
Then later loop over indexes:
indexes.map(index => {
this.state.compArr[index] ===> ??stuck here??
})
Expected Output: So on an array of three instances, say I want the second one only to have the class of backgroundColor:
<div className='box' </div>
<div className='box backgroundColor'</div>
<div className='box'</div>
Only in my case, I want the instances to correlate with the indexes array
How can I set the conditional class to true in those instances I have in the array? Say I have like 500+ instances total.
If I understand you correctly this is proably what you're looking for...
let indexes = [101, 178, 232, 545]
let compArr = this.arr.map((obj, i) => {
return (
<div
className={`box${( indexes.includes(i) ? " backgroundColor" : "")}`}
key={i}
>
</div>
);
})
this.setState({
compArr: compArr
})
If your goal is only to return the given div with the className backgroundColor for every element in this.arr with its index included in the indexes array then...
You only need to iterate over the array once and can perform all the necessary logic you're currently doing performing in two.
You don't need to use Array.push() to an outside collector when using any of the pure functions like map() or reduce() because they return a new version of whatever array is iterated over.
If for whatever reason you want a distinct array of <div>s with an index in indexes then you should use reduce...
let compArr = this.arr.reduce((acc, obj, i) => {
if (!indexes.includes(i)) return acc;
return [
...acc,
<div className="box backgroundColor" key={i}></div>
)]
},[])

Resources