Since I am new to React, I am unable to understand why the below code is not showing the array bars correctly.
After inspecting, I realised that the code is not even generating the 'array-bar' div correctly.
Can somebody help in why this is not working ?
P.S.- This is an array visualiser which visualizes a random array.
import React, { Component } from "react";
import "./SortingViz.css";
export default class SortingViz extends Component {
constructor(props) {
super(props);
this.state = {
array: [],
};
}
componentDidMount() {
this.resetArray();
}
resetArray() {
let array = [];
for (let i = 0; i < 150; i++) {
array.push(randomInt(100,600));
}
this.setState(array);
console.log(array);
}
render() {
const { array } = this.state;
return (
<div className="array-container">
{array.map((value, idx) => {
<div className="array-bar"
key={idx}
style={{ height: `${value}px` }}></div>;
})}
<button onClick={() => this.resetArray}>Generate</button>
</div>
);
}
}
function randomInt(min, max)
{
return Math.floor(Math.random() * (max - min + 1)) + min;
}
Here is the CSS for ref.
.array-container{
margin: 0 auto;
}
.array-bar{
background-color: blue;
width: 5px;
}
You need to return the div from the map callback
render() {
const { array } = this.state;
return (
<div className="array-container">
{array.map((value, idx) => {
return <div className="array-bar"
key={idx}
style={{ height: `${value}px` }}></div>;
})}
<button onClick={() => this.resetArray}>Generate</button>
</div>
);
}
The reason is what #Mina says.
But given you only return an element and don't do anymore, you could Aldo do it by changing {} for () in your callback. That way indicates you return all surrender by (...)
render() {
const { array } = this.state;
return (
<div className="array-container">
{array.map((value, idx) => (
<div className="array-bar"
key={idx}
style={{ height: `${value}px` }}></div>;
))}
<button onClick={() => this.resetArray}>Generate</button>
</div>
);
}
Related
I am trying to build a sorting Visualizer using React. Right now, I am implementing BubbleSort. The program looks like this:
Here's the code:
class Sorter extends Component {
state = {
array: [100,4,214,55,11,22,10,33],
color: "blueviolet",
}
bubblesorter = async () => {
let arr = this.state.array
var len = arr.length,
i, j, stop;
for (i=0; i < len; i++){
for (j=0, stop=len-i; j < stop; j++){
if (arr[j] > arr[j+1]){
swap(arr, j, j+1);
}
await new Promise(resolve => setTimeout(resolve, 100));
this.setState({array:arr})
}
}
}
render() {
const array = this.state.array
return (
<div>
<h1>This is a sorter</h1>
<div className="container">
{array.map((value, id) => (
<span>
<div className="bar" key={id} style={{height: value+"px", backgroundColor: this.state.color}} >
</div>
</span>
))}
</div>
<button onClick={this.bubblesorter}>Sort</button>
</div>
)
}
The sorting functionality works correctly. But I would like to change the color of bars (array elements being compared). Can someone help with the Logic which can be used to implement this...Thanks
You can make the colors array and update the color which was only changed.
class Sorter extends Component {
state = {
array: [100, 4, 214, 55, 11, 22, 10, 33],
colors: Array(8).fill('blueviolet'), // Array of colors for each bar
};
bubblesorter = async () => {
...
await new Promise((resolve) => setTimeout(resolve, 100));
// Set different item's color as `red`
const colors = arr.map((item, index) => (this.state.array[index] === item ? 'blueviolet' : 'red'));
this.setState({ array: arr, colors });
}
}
};
render() {
const { array, colors } = this.state;
return (
<div>
<h1>This is a sorter</h1>
<div className='container'>
{array.map((value, id) => (
<span>
<div
className='bar'
key={id}
// <- Use the color at the same index of the item
style={{ height: value + 'px', backgroundColor: colors[id] }}
></div>
</span>
))}
</div>
<button onClick={this.bubblesorter}>Sort</button>
</div>
);
}
}
Tried this...it works but can be improved using modern javascript (not that familiar with it, suggestions welcome)
class Sorter extends Component {
state = {
array: [],
colors: Array(8).fill('blueviolet'),
}
bubblesorter = async () => {
...
await new Promise(resolve => setTimeout(resolve, 1000));
const colors = []
for (let k=0; k<8; k++) {
let color = k==j || k==j+1 ? 'red':'blueviolet'
colors[k] = color
}
// console.log(colors)
this.setState({ array: arr, colors: colors });
}
}
render()
{
// const array = this.state.array
const { array, colors } = this.state;
// console.log(array)
return (
<div>
<h1>This is a sorter</h1>
<div className="container">
{array.map((value, id) => (
<span>
<div
className="bar"
key={id}
style={{ height: value + 'px', backgroundColor: colors[id]
}} >
</div>
{/* <span>{value}</span> */}
</span>
))}
</div>
<button onClick={this.bubblesorter}>Sort</button>
</div>
)
}
}
I develop cards in react js that card automatically generated based on API data but that all card shows one below other and I want to design that card in row fashion. I try many ways to design that card in row way but it is not done. I try inline CSS, some react-bootstrap class. So please give me some suggestions about how I can design this card.
class App extends React.Component {
elements = [];
state = {
data:[ null ]
};
getBikes = () => {
try {
return axios.get('URL')
} catch (error) {
console.error(error)
}
}
constructor(props) {
super(props);
this.props = props;
//this.elements=["one", "two", "three", "four"];
}
componentDidMount() {
const breeds = this.getBikes()
.then(response => {
if (response) {
console.log(response.data.message)
var arr = (response.data.message);
//var elements = [];
for (var i = 0; i < arr.length; i++) {
console.log(arr[i].bikeId)
this.elements.push(<div>
<Cards value={arr[i]} />
</div>);
}
this.setState({ data: arr[0].bikeId })
}
})
.catch(error => {
console.log(error)
})
}
render() {
console.log("printitng")
//const array = [1,2];
return (
<div style={{ display: "flex" }}>
<h1>{'This will always render'}</h1>
{this.state && this.state.data &&
this.state.map((item, index) => {
return <div key={index} style={{ width: "300px" }}>
<Cards elements={this.elements} /> //pass your props value
</div>
})
// < div >
// <Cards elements={this.elements} /> //pass your props value
// </div>
}
</div>
)
}
}
There's just one correction. You've pushed the elements of array with div which is a block element. You need push the elements with span or you can give class to the div and change from block to flex or inline block.
componentDidMount() {
const breeds = this.getBikes()
.then(response => {
if (response) {
console.log(response.data.message)
var arr = (response.data.message);
//var elements = [];
for (var i = 0; i < arr.length; i++) {
console.log(arr[i].bikeId)
// Changed from div to span
this.elements.push(<span>
<Cards value={arr[i]} />
</span>);
}
this.setState({ data: arr[0].bikeId })
}
})
.catch(error => {
console.log(error)
})
}
render() {
console.log("printitng")
return (
<div>
<h1>{'This will always render'}</h1>
{this.state && this.state.data &&
<div>
<Cards elements={this.elements} /> //pass your props value
</div>
}
</div>
)
}
}
class App extends React.Component {
constructor(props) {
super(props);
this.state = {
bikeId : null,
data : []
}
}
getBikes = () => {
try {
return axios.get('URL')
} catch (error) {
console.error(error)
}
}
componentDidMount() {
const breeds = this.getBikes()
.then(response => {
if (response) {
this.setState({ data: response.data.message, bikeId: response.data.message[0].bikeId })
}
})
.catch(error => {
console.log(error)
})
}
render() {
return (
<div style={{ display: "flex" }}>
{
this.state.data.map((item, index) => {
return <div key={index} style={{ width: "300px" }}>
<Cards value={item} />
</div>
})
}
</div>
)
}
}
give parent class flex property then you can add as much child in a single row you can add.
I'm trying to implement a Trello with virtual lists and different sized items, using react-beautiful-dnd and react-virtuoso (virtual lists with automated calculation of item sizes).
react-virtuoso is not part of the examples of beautiful-react-dnd, I'm facing 2 issues:
I can't scroll while dragging an item
I get this error often: Invariant failed: Can only recollect Droppable client for Droppables that have a scroll container.
Here's a codesandbox
DroppableList.tsx
import * as React from "react";
import { useState } from "react";
import "./styles.css";
import { Virtuoso } from "react-virtuoso";
import {
Draggable,
DragDropContext,
Droppable,
DropResult,
ResponderProvided
} from "react-beautiful-dnd";
import { Item } from "./Item";
import { reorder } from "./App";
import { createItemList } from "./data";
import { ItemList, ItemType } from "./dtos";
const itemCount = 30;
export const VirtualDragDropList = () => {
const [itemList, setItemList] = useState<ItemList>(createItemList(itemCount));
const onDragEnd = (result: DropResult, provided: ResponderProvided) => {
// dropped outside the list
if (!result.destination) {
return;
}
const items = reorder(
itemList,
result.source.index,
result.destination.index
);
setItemList(items);
};
return (
<DragDropContext onDragEnd={onDragEnd}>
<Droppable
droppableId="droppable"
mode="virtual"
renderClone={(provided, snapshot, rubric) => {
// console.log("provided", provided);
// console.log("snapshot", snapshot);
// console.log("rubric", rubric);
return (
<Item
itemData={itemList[(rubric as any).source.index]}
provided={provided}
index={(rubric as any).source.index} // typing seems wrong, hence the any.
/>
);
}}
>
{droppableProvided => (
<div ref={droppableProvided.innerRef}>
<Virtuoso
style={{ width: "300px", height: "400px" }}
totalCount={itemCount}
// item={index => <Item itemData={itemList[index]} />}
item={index => <Row itemData={itemList[index]} index={index} />}
/>
</div>
)}
</Droppable>
</DragDropContext>
);
};
const Row = React.memo((args: { itemData: ItemType; index: number }) => {
const { itemData, index } = args;
return (
<Draggable draggableId={itemData.id} index={index} key={itemData.id}>
{(provided, snapshot) => (
<Item itemData={itemData} index={index} provided={provided} />
)}
</Draggable>
);
});
Item.tsx
import * as React from "react";
import { ItemType } from "./dtos";
export const Item: React.FC<{
index?: number;
itemData: ItemType | undefined;
provided?: any;
}> = props => {
const height = (props.itemData ? props.itemData.height : 10) * 3;
const style = {
margin: ".3rem",
padding: ".3rem",
display: "flex",
border: "1px solid lightgrey",
height: `${height}px`
};
return (
<div
ref={props.provided && props.provided.innerRef}
{...props.provided && props.provided.draggableProps}
{...props.provided && props.provided.dragHandleProps}
style={{ ...props.provided.draggableProps.style, ...style }}
>
{props.itemData && props.itemData.text}
</div>
);
};
data.ts
import { ItemList } from "./dtos";
export const createItemList = (itemCount: number): ItemList => {
const itemList: ItemList = [];
for (let i = 0; i < itemCount; i++) {
itemList.push({
id: i.toString(),
text: `Item ${i}`,
height: Math.random() * 20
});
}
return itemList;
}
remove ref={droppableProvided.innerRef} from div and add
scrollerRef={droppableProvided.innerRef} to virtuoso
Following is the UI, in which each box I am trying to display after 1 sec delay - (Box1, 1 sec delay, Box2, 1 sec delay, Box3 ..so on)
Instead I am getting -
My React code and let me know what I am doing wrong here & why its showing numbers -
const CreateBox = (props) => {
return (
<>
{/*<div className="box">{props.num}</div>*/}
<div className="box"></div>
</>
)
}
const App = () => {
return (
<div className="app">
<h3>App</h3>
{
[1,2,3,4,5,6,7,8,9,10].map((item) => {
return setTimeout(() => {
// return (<CreateBox num={item} />)
return (<CreateBox />)
}, 1000)
})
}
</div>
)
}
const root = document.querySelector('#root')
ReactDOM.render(<App />, root)
Codepen - https://codepen.io/anon/pen/pBLPMY
Instead of creating a new timeout for every element in the array on every render, you could create an interval in componentDidMount and increment a number in your state until it reaches 10 and use this number in your render method instead.
Example
class App extends React.Component {
state = {
count: 0
};
componentDidMount() {
const interval = setInterval(() => {
this.setState(
({ count }) => ({ count: count + 1 }),
() => {
if (this.state.count === 10) {
clearInterval(interval);
}
}
);
}, 1000);
}
render() {
return (
<div className="app">
<h3>App</h3>
{Array.from({ length: this.state.count }, (_, index) => (
<CreateBox key={index} num={index + 1} />
))}
</div>
);
}
}
const CreateBox = props => {
return <div className="box">{props.num}</div>;
};
ReactDOM.render(<App />, document.getElementById("root"));
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.6.3/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.6.3/umd/react-dom.production.min.js"></script>
<div id="root"></div>
Don't use setTimeout while looping. Instead set the timer inside the CreateBox component using state. If you remove the timeout you can see the boxes. To handle the delay pass the index * 1000 as a timer for each element.
class CreateBox extends React.Component {
state = {
opacity: 0
}
constructor(props){
super(props)
}
componentDidMount(){
setTimeout(()=> this.setState({opacity: 1}),`${this.props.time}000`)
}
render() {
console.log(this.props)
return (
<div style={this.state} className="box">{this.props.num}</div>
)
}
};
const App = () => {
return (
<div className="app">
<h3>App</h3>
{
[1,2,3,'w',5,6,7,8,9,10].map((item, index) => <CreateBox num={item} time={index}/>)
}
</div>
)
}
const root = document.querySelector('#root')
ReactDOM.render(<App />, root)
const CreateBox = (props) => {
return (
<div className="box">{props.num}</div>
)
}
const App = () => {
return (
<div className="app">
<h3>App</h3>
{
[1,2,3,4,5,6,7,8,9,10].map((item) => {
return (<CreateBox num={item} />)
})
}
</div>
)
}
const root = document.querySelector('#root')
ReactDOM.render(<App />, root)
I have a component that switches some content and the animation of the content depending on the side it is switching it from:
import React, { Component } from "react";
class Skills extends Component {
constructor(props) {
super(props);
this.state = {
shownSkill: 0,
fallIn: true,
slideUp: false
};
}
getPreviousSkill = () => {
const { shownSkill } = this.state;
const newSkill = shownSkill < 1 ? 3 : shownSkill - 1;
this.updateShownSkill(newSkill, false);
};
getNextSkill = () => {
const { shownSkill } = this.state;
const newSkill = shownSkill > 2 ? 0 : shownSkill + 1;
this.updateShownSkill(newSkill, true);
};
updateShownSkill = (skillIndex, fallIn) => {
this.setState({
shownSkill: skillIndex,
fallIn: fallIn,
slideUp: !fallIn
});
};
getSkillData = () => {
const { skills } = this.props;
const { shownSkill } = this.state;
return skills[shownSkill];
};
render() {
const { name, skill, description } = this.getSkillData();
const { shownSkill, slideUp } = this.state;
const { skills } = this.props;
return (
<div className="route-container skills">
<div className="skills-content-container">
{slideUp ? (
<div className="skills-right-content slide-up">
<div className="subtitle">{name}</div>
{description.map((p, i) => (
<div className="text" key={i}>
{p}
</div>
))}
</div>
) : (
<div className="skills-right-content
fall-in">
<div className="subtitle">{name}</div>
{description.map((p, i) => (
<div className="text" key={i}>
{p}
</div>
))}
</div>
)}
</div>
</div>
);
}
}
export default Skills;
Then I am animating the .fall-in class with css:
#keyframes fall-in {
0% {
margin-top: -600px;
}
100% {
margin-top: 0;
}
}
.fall-in {
animation-name: fall-in;
animation-duration: 0.5s;
animation-timing-function: linear;
animation-iteration-count: 1;
}
I would like this animation to trigger once every time the content of the .subtitle and .text divs changes, regardless of whether or not the animation changed.
This example will only trigger the animation the first time the css class is added.
Hey maybe you want to give a try on my OSS.
https://github.com/bluebill1049/react-simple-animate
I think it does what you want above, maybe worth to give it a try?
import Animate from 'react-simple-img';
import React from 'react';
export default ({ready}) => {
return <Animate startAnimation={ready} startStyle={{
marginTop: '-600px',
}} endStyle={{
marginTop: '0',
}}>
<YourComponent />
</Animate>
};