Using .map() to generate elements with Refs - javascript

Is there a best way to go about using .map() to output elements with refs?
I'm doing this so I can track the elements (generated from state) for collision detection in an endless runner style game (without canvas).
I’ve tried 3 different approaches and matter what I do I seem to get ref=ref() when it should be ref=“tango” (as per the state). Image here - https://i.imgur.com/oeStcHb.png
EDIT: for others in future, I have since learned that ref=ref() is expected behaviour to indicate a callback is associated with it, instead of the old way of showing the actual ref.
Code being used-
this.state = {
people: {
tango: { height: 10, weight: 200 },
cash: { height: 20, weight: 300 }
}
};
...
outputStatePeople(key) {
return(<span className="db" key={key} ref={key}>name: {key}</span>)
}
...
render() { return (
<div>
{Object.keys(this.state.people).map(key => this.outputStatePeople(key))}
</div>
)}
The above use a function to generate dom, but this also happens if outputting inline html and when using a component.
Any help would be greatly appreciated! 😀

Define a function for a ref and store the refs in the class variable object like
constructor(props) {
super(props);
this.state = {
people: {
tango: { height: 10, weight: 200 },
cash: { height: 20, weight: 300 }
}
};
this.domRefs = {};
}
outputStatePeople(key) {
return (
<span className="db" key={key} ref={(ref) => {this.domRefs[key] = ref}}>
name: {key}, height: {this.state.people[key].height}
</span>
);
}
You can read more about Refs and Dom Here

Related

useRef([]) with dynamically created elements

I created a component with several div elements.
By adding a ?goto= parameter to the url I want to scroll the the relevant element. I now solved that with const itemsRef = useRef([]);.
My main concern now is if that's the right and performance efficient approach with itemsRef.current[element.id] = el. element.id will be unique for each element.
I also found packages such as: https://github.com/Macil/react-multi-ref
But I don't see the disadvantage of my approach yet.
Here you can find my current solution in action: https://codesandbox.io/s/scrolltoref-w5i7m?file=/src/Element.js
import React, { useRef, useEffect, useState } from "react";
import clsx from "clsx";
const blueprint = [
{
id: "3mD59WO",
name: "AUDITORIUM",
position: 0,
rooms: [
{
id: "zR8Qgpj",
name: "Audimax",
subtitle: null,
details: null,
position: 0,
elements: [
{
id: "1jLv04W",
position: 0,
type: "daily",
element: "listing_large",
properties: {
meetingId: null,
capacity: 6
}
},
{
id: "1jLv12W",
position: 1,
type: "daily",
element: "listing_large",
properties: {
meetingId: null,
capacity: 6
}
}
]
}
]
},
{
id: "4mDd9WO",
name: "FOYER",
position: 1,
rooms: [
{
id: "4R8Qgpj",
name: "Speakers Table",
subtitle: null,
details: null,
position: 0,
elements: [
{
id: "2jLv04W",
position: 0,
type: "daily",
element: "listing_large",
properties: {
meetingId: null,
capacity: 6
}
},
{
id: "2jLv12W",
position: 1,
type: "daily",
element: "listing_large",
properties: {
meetingId: null,
capacity: 6
}
}
]
}
]
}
];
export default function Query() {
const itemsRef = useRef([]);
const [currentRef, setCurrentRef] = useState();
useEffect(() => {
const scrollToRef = ref => {
window.scrollTo(0, ref.offsetTop);
};
const goto = "1jLv12W"; // This will become an URL parameter ?goto=:someID in the final version
const ref = itemsRef.current[goto];
setCurrentRef(ref); // This is needed to change the className to highlight
scrollToRef(ref); // Here I assign the ref and the component should scroll to that ref
}, []);
return (
<div key="element">
{blueprint.map(floor => (
<div key={floor.id} style={{ marginTop: 50 }}>
Floor: {floor.name} <br />
<br />
{floor.rooms.map(room => (
<div key={room.id}>
Room Name: {room.name}
<br />
{room.elements.map(element => (
<div
ref={el => (itemsRef.current[element.id] = el)}
className={clsx({
highlight:
currentRef && currentRef === itemsRef.current[element.id]
})}
key={element.id}
style={{ backgroundColor: "green", marginTop: 100 }}
>
ElementID: {element.id}
<br />
</div>
))}
</div>
))}
</div>
))}
</div>
);
}
That's the right approach, usually, you will see useRef([]) when handling multiple animations in a page, and that's exactly how it's done itemsRef.current[element.id] = el.
My main concern now is if that's the right and performance efficient approach
That's directly related to "Why Premature Optimization Is the Root of All Evil".
Premature optimization is spending a lot of time on something that you may not actually need.
You trying to optimize before you have any performance issues. Focus on delivering the product and clean code, find a time for optimization when you actually measured it.
We also don’t want to waste an enormous amount of time doing performance optimization on things that don’t matter. Many development teams get caught up in focusing on optimizing for performance and scale before they have validated their new product functionality.
useRef is basically the same as doing useState({ current: <value you pass in> });
Given your use case, what you have done is sufficient however I would change use ref to initialise with an object since that is what you are actually using as oppose to an array:
const itemsRef = useRef({});
Your code still works but may potentially give you some unexpected behaviour since assigning properties to an array can be a bit weird and is definitely not what you intend to do anyway.
For example with an array you are actually doing this:
[]["<some id>"] = el; // very weird!
vs an object:
{}["<some id>"] = el

Not rendering JSX from function in React

The function is getting the value of a button click as props. Data is mapped through to compare that button value to a key in the Data JSON called 'classes'. I am getting all the data correctly. All my console.logs are returning correct values. But for some reason, I cannot render anything.
I've tried to add two return statements. It is not even rendering the p tag with the word 'TEST'. Am I missing something? I have included a Code Sandbox: https://codesandbox.io/s/react-example-8xxih
When I click on the Math button, for example, I want to show the two teachers who teach Math as two bubbles below the buttons.
All the data is loading. Just having an issue with rendering it.
function ShowBubbles(props){
console.log('VALUE', props.target.value)
return (
<div id='bubbles-container'>
<p>TEST</p>
{Data.map((item,index) =>{
if(props.target.value == (Data[index].classes)){
return (
<Bubble key={index} nodeName={Data[index].name}>{Data[index].name}
</Bubble>
)
}
})}
</div>
)
}
Sandbox Link: https://codesandbox.io/embed/react-example-m1880
import React, {Component} from 'react';
import ReactDOM from 'react-dom';
const circleStyle = {
width: 100,
height: 100,
borderRadius: 50,
fontSize: 30,
color: "blue"
};
const Data = [
{
classes: ["Math"],
name: "Mr.Rockow",
id: "135"
},
{
classes: ["English"],
name: "Mrs.Nicastro",
id: "358"
},
{
classes: ["Chemistry"],
name: "Mr.Bloomberg",
id: "405"
},
{
classes: ["Math"],
name: "Mr.Jennings",
id: "293"
}
];
const Bubble = item => {
let {name} = item.children.singleItem;
return (
<div style={circleStyle} onClick={()=>{console.log(name)}}>
<p>{item.children.singleItem.name}</p>
</div>
);
};
function ShowBubbles(props) {
var final = [];
Data.map((item, index) => {
if (props.target.value == Data[index].classes) {
final.push(Data[index])
}
})
return final;
}
function DisplayBubbles(singleItem) {
return <Bubble>{singleItem}</Bubble>
}
class Sidebar extends Component {
constructor(props) {
super(props);
this.state = {
json: [],
classesArray: [],
displayBubble: true
};
this.showNode = this.showNode.bind(this);
}
componentDidMount() {
const newArray = [];
Data.map((item, index) => {
let classPlaceholder = Data[index].classes.toString();
if (newArray.indexOf(classPlaceholder) == -1) {
newArray.push(classPlaceholder);
}
// console.log('newArray', newArray)
});
this.setState({
json: Data,
classesArray: newArray
});
}
showNode(props) {
this.setState({
displayBubble: true
});
if (this.state.displayBubble === true) {
var output = ShowBubbles(props);
this.setState({output})
}
}
render() {
return (
<div>
{/* {this.state.displayBubble ? <ShowBubbles/> : ''} */}
<div id="sidebar-container">
<h1 className="sidebar-title">Classes At School</h1>
<h3>Classes To Search</h3>
{this.state.classesArray.map((item, index) => {
return (
<button
onClick={this.showNode}
className="btn-sidebar"
key={index}
value={this.state.classesArray[index]}
>
{this.state.classesArray[index]}
</button>
);
})}
</div>
{this.state.output && this.state.output.map(item=><DisplayBubbles singleItem={item}/>)}
</div>
);
}
}
ReactDOM.render(<Sidebar />, document.getElementById("root"));
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.0.0/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.0.0/umd/react-dom.production.min.js"></script>
<div id="root"></div>
The issue here is ShowBubbles is not being rendered into the DOM, instead (according the sandbox), ShowBubbles (a React component) is being directly called in onClick button handlers. While you can technically do this, calling a component from a function will result in JSX, essentially, and you would need to manually insert this into the DOM.
Taking this approach is not very React-y, and there is usually a simpler way to approach this. One such approach would be to call the ShowBubbles directly from another React component, e.g. after your buttons using something like:
<ShowBubbles property1={prop1Value} <etc...> />
There are some other issues with the code (at least from the sandbox) that you will need to work out, but this will at least help get you moving in the right direction.

React - changing the background of a single span class not working

I am new to React so my apologies if the question, or the thing I am trying to achieve is just weird (and please do tell if there is a better / more logic way to do this).
I am using the List Fabric React component in my React application, which is based on the ListGridExample component which is found here:
https://developer.microsoft.com/en-us/fabric#/components/list
I have set it up but I can't seem to accomplish the following:
When a span class (which is actually an item) in the List component is clicked, I want to change it's background color, to do this I have followed the instructions in the following post:
https://forum.freecodecamp.org/t/react-js-i-need-a-button-color-to-change-onclick-but-cannot-determine-how-to-properly-set-and-change-state-for-that-component/45168
This is a fairly simple example but this changes all my grid cells / span classes to the color blue instead of only the clicked one. Is there a way I can make just the clicked span class change it's background?
The Initial state:
The state after clicking one span class (which is wrong):
Implementation code (ommitted some unecesary code):
class UrenBoekenGrid extends React.Component {
constructor(props) {
super(props);
this.state = {
bgColor: 'red'
}
}
render() {
return (
<FocusZone>
<List
items={[
{
key: '#test1',
name: 'test1',
},
{
name: 'test2',
key: '#test2',
},
{
name: 'test3',
key: '#test3',
},
{
name: 'test4',
key: '#test4',
},
..... up to 32 items
]}
onRenderCell={this._onRenderCell}
/>
</FocusZone>
);
}
changeColor(item){
this.setState({bgColor: 'blue'});
console.log('clicked item == ' + item.name)
}
_onRenderCell = (item, index) => {
return (
<div
className="ms-ListGridExample-tile"
data-is-focusable={true}
style={{
width: 100 / this._columnCount + '%',
height: this._rowHeight * 1.5,
float: 'left'
}}
>
<div className="ms-ListGridExample-sizer">
<div className="msListGridExample-padder">
{/* The span class with the click event: */}
<span className="ms-ListGridExample-label" onClick={this.changeColor.bind(this, item)} style={{backgroundColor:this.state.bgColor}}>{`item ${index}`}</span>
<span className="urenboeken-bottom"></span>
</div>
</div>
</div>
);
};
}
I now have attached the click event to the span class itself but I would think it is way more logic to have the click event on the item(s) (array) itself, however I could not find a way to achieve this either.
----UPDATE----
#peetya answer seems the way to go since #Mario Santini answer just updates a single cell, if another cell is clicked then the previous one returns back to normal and loses it's color.
So what I did is adding the items array to the state and adding the bgColor property to them:
this.state = {
items: [
{
key: '#test1',
name: 'test1',
bgColor: 'blue',
},
{
name: 'test2',
key: '#test2',
bgColor: 'blue',
},
{
name: 'test3',
key: '#test3',
bgColor: 'blue',
},
{
name: 'test4',
key: '#test4',
bgColor: 'blue',
},
],
}
Now in my List rendering I have set the items to the state items array and added the onClick event in the _onRenderCell function:
render() {
return (
<FocusZone>
<List
items={this.state.items}
getItemCountForPage={this._getItemCountForPage}
getPageHeight={this._getPageHeight}
renderedWindowsAhead={4}
onRenderCell={this._onRenderCell}
/>
</FocusZone>
);
}
_onRenderCell = (item, index) => {
return (
<div
className="ms-ListGridExample-tile"
data-is-focusable={true}
style={{
width: 100 / this._columnCount + '%',
height: this._rowHeight * 1.5,
float: 'left'
}}
>
<div className="ms-ListGridExample-sizer">
<div className="msListGridExample-padder">
<span className="ms-ListGridExample-label"
onClick={this.onClick(item.name)}
style={{backgroundColor: item.bgColor}}
>
{`item ${index}`}
</span>
<span className="urenboeken-bottom"></span>
</div>
</div>
</div>
);
};
The problem is that I can't add the onClick event in the _onRenderCell function as this will give the following error:
I want to keep the Fabric List component as it also has functions for rendering / adjusting to screen size, removing the list component entirely and just replacing it with what #peetya suggested works:
render() {
<div>
{this.state.items.map(item => (
<div onClick={() => this.onClick(item.name)} style={{backgroundColor: item.bgColor}}>
{item.name}
</div>
))}
</div>
}
But this will also remove the List component functionality with it's responsive functions.
So my last idea was to just replace the items of the List with the entire onClick div and removing the _onRenderCell function itself, but this makes the page blank (can't see the cells at all anymore..):
render() {
return (
<FocusZone>
<List
items={this.state.items.map(item => (
<div onClick={() => this.onClick(item.name)} style={{backgroundColor: item.bgColor}}>
{item.name}
</div>
))}
getItemCountForPage={this._getItemCountForPage}
getPageHeight={this._getPageHeight}
renderedWindowsAhead={4}
// onRenderCell={this._onRenderCell}
/>
</FocusZone>
);
}
I thought that perhaps the css ms-classes / div's should be in there as well because these have the height/width properties but adding them (exactly as in the _onRenderCell function) does not make any difference, the page is still blank.
The problem is that you are storing the background color in the state of the Grid and assign this state to every element of the grid, so if you update the state, it will affect every element. The best would be if you create a separate component for the Grid elements and store their own state inside there or if you want to use only one state then store the items array inside the state and add a new bgColor attribute for them so if you want to change the background color only for one item, you need to call the setEstate for the specific object of the items array.
Here is a small example (I did not tested it):
class UrenBoekenGrid extends React.Component {
constructor(props) {
super(props);
this.state = {
items: [
{
key: '#test1',
name: 'test1',
bgColor: 'blue',
},
],
};
}
onClick(name) {
this.setState(prevState => ({
items: prevState.items.map(item => {
if (item.name === name) {
item.bgColor = 'red';
}
return item;
})
}))
}
render() {
<div>
{this.state.items.map(item => (
<div onClick={() => this.onClick(item.name)} style={{backgroundColor: item.bgColor}}>
{item.name}
</div>
))}
</div>
}
}
Actually you are changing the color of all the span elements, as you set for each span the style to the state variable bgColor.
Insteas, you should save the clicked item, and decide the color based on that:
this.state = {
bgColor: 'red',
clickedColor: 'blue
}
In the constructor.
Then in the click handler:
changeColor(item){
this.setState({selected: item.name});
console.log('clicked item == ' + item.name)
}
So in the renderer (I just put the relevant part):
<span ... style={{backgroundColor: (item.name === this.state.selected ? this.state.clickedColor : this.state.bgColor)}}>{`item ${index}`}</span>

Dynamically add or subtract array of children?

I am new to React and have been trying to figure out how to control an array of components from a parent component. My job is to create a site where I can add or subtract names to a list, but have gotten stuck on the best way to do it. In this case, I created an array of react components, each with controlled input for title boxes and each with a delete button that would call the parent function's remove function through the prop system. However, I noticed that when doing so, the array in the parent function would remain correct, while the id's of the children components would not change to be reordered, thereby ruining subsequent removals. I am sure I am doing this wrong and would like to find a better and more efficient way of doing this. Thanks!
import React, {Component} from 'react';
import Music from './music'
import axios from 'axios';
var childrenComponents = [];
class Selection {
constructor(){
this.music = '';
this.beginning = 0;
this.the_end = 0;
}
setTitle=(title)=>{
this.music = title;
}
setStart=(start)=>{
this.beginning = start;
}
setEnd=(end)=>{
this.the_end = end;
}
}
class Practice extends React.Component{
constructor(props){
super(props);
this.state = {
number_music: 0,
number: 0,
selections: Array(0).fill(null),
deletions: 0,
}
this.addAnotherSong = this.addAnotherSong.bind(this);
this.removeSong = this.removeSong.bind(this);
this.renderMusicPlayed = this.renderMusicPlayed.bind(this);
}
removeSong(index){
if((this.state.number_music-1) >= 0){
alert(index);
for(var i = 0; i < (this.state.selections.length-1); i++){
console.log(this.state.selections[i].music);
}
childrenComponents.splice(index, 1);
this.setState({selections: this.state.selections.filter((_, i) => i !== index),
number_music: this.state.number_music - 1,
deletions: this.state.deletions += 1});
console.log("========================");
for(var i = 0; i < (this.state.selections.length-1); i++){
console.log(this.state.selections[i].music);
}
console.log("///////////////////////////////////////////////////");
}
}
addAnotherSong(){
this.state.selections.push(new Selection());
var i = this.state.number_music;
childrenComponents.push(
<Music key={i} number={i} subtract={this.removeSong}
Title={this.state.selections[i].music} Start={this.state.selections[i].beginning}
End={this.state.selections[i].the_end} changeTitle={this.state.selections[i].setTitle}
changeStart={this.state.selections[i].changeStart} changeEnd={this.state.selections[i].changeEnd}/>
);
this.setState({ number_music: this.state.number_music += 1, number: this.state.number += 1});
}
renderMusicPlayed(){
return (
<div>
{childrenComponents}
</div>
);
}
render(){
return(
<div>
<button onClick={()=> this.props.practice()}>Log Practice Session</button>
<h1>{this.props.time}</h1>
<form >
Description: <input type="form" placeholder="How did it go?" name="fname"/><br/>
</form>
{this.renderMusicPlayed()}
<button onClick={()=>this.addAnotherSong()}>Add Another Piece</button>
{this.state.number_music}
</div>
);
}
}
export default Practice;
That is the parent.
This is the Child:
import React, {Component} from 'react';
import InputBox from './input';
class Music extends React.Component{
constructor(props){
super(props);
this.state = {
title: null,
start: null,
end: null
}
}
componentWillReceiveProps(props){
this.setState({ title: this.props.Title});
}
render(){
return(
<div>
<InputBox initialValue={this.props.number} cValue={this.props.Title} identity={this.props.number} updateInput={this.props.changeTitle} />
<InputBox initialValue="Starting Measure" cValue={this.props.Start} identity={this.props.number} updateInput={this.props.changeStart} />
<InputBox initialValue="Ending Measure" cValue={this.props.End} identity={this.props.number} updateInput={this.props.changeEnd} />
<button onClick={()=> this.props.subtract(this.props.number)}>Delete</button>
</div>
)
}
}
export default Music;
And this is the grand child so to speak:
import React,{Component} from 'react';
class InputBox extends React.Component{
constructor(props){
super(props);
this.state = { value: this.props.initialValue, text: "" }
this.handleChange = this.handleChange.bind(this);
}
handleChange(event){
this.setState({value: event.target.value});
this.props.updateInput(this.state.value, this.props.identity);
}
render(){
return(
<input type="text" onChange={this.handleChange} value={this.state.cValue}></input>
)
}
}
export default InputBox;
I guess my main question is what is the ideal way for handling this kind of problem.
The reason your IDs are not changing is because you're pushing fully formed components to the array.
Imagine we have 3 components - formatting will be a little weird, but hopefully it illustrates the point:
[ Music: { id: 0 }, Music: { id: 1 }, Music: { id: 2 } ]
When we click the delete button, say on Music with id: 1, we end up with this:
[ Music: { id: 0 }, Music: { id: 2 } ]
We spliced the right Music out, but we now have a wrong index - we never actually changed the Music with id: 2. It would be much easier (in my opinion) to just dynamically construct your Music components in the render function.
Realistically, your childrenComponents array isn't all that useful - the Music components created in it are all created with the index i in mind:
Title={this.state.selections[i].music}
Start={this.state.selections[i].beginning}
End={this.state.selections[i].the_end}
and so on and so forth.
We could simplify this pretty easily, and consolidate all of this into one array.
Imagine we had an array field state.children, which looked something like this:
[
{ title: _____, start: _____, end: ____, ... },
{ title: _____, start: _____, end: ____, ... },
{ title: _____, start: _____, end: ____, ... },
]
This is a lot more clear in a huge way: our data is consolidated in one, singular place, and we aren't tying them together by some arbitrary index. You've done this in a sense with your selections array, but because you are also using childrenComponents, you're double managing what is essentially the same data.
We can pretty easily render it, too, with something along the lines of:
render() {
{
this.state.children.map((child, index) => (
<Music key={index}
number={index}
subtract={this.removeSong}
Title={this.state.children[index].title}
...
/>
);
}
}
That helps us decouple the actual meat of our objects (title, beginning, ending, etc) from their position in the array, which don't really mean anything and are just getting in the way here. That lets us splice up our array however we see fit, and be certain that we're not breaking any relationships between our components and their indexes.

How to appendTo React Components as simple as jQuery can

As I try to get into React I find I'm running through lists of things I can do very simply in jQuery but are an absolute nightmare in React.
https://codepen.io/PocketNinjaDesign/pen/boJoEd
have modified the goal from 2 elements to nth
The goal is that nth elements on the page can popup inside of any element / component I want on the page. I achieve this the good old fashion way by adding a data attribute containing Json with an array of components to appendTo.
You can also use JS to turn a component into a ninja and passing an object through containing the list of components to appear in.
$(function() {
$('.other-ninja').Ninja({
components: ['title', 'header', 'angry']
});
});
So, imagining these are React Components now, all different kinds of components, but the ninja block(s) can be told they can appear in any components they want.
How is that possible with React without going through a ball ache of declaration and indigestion?
Here is my code for the jQuery popup appendTo script as what I want to achieve in React.
Have altered the code since the answer I received as I don't think I
was putting my question across correctly.
HTML
<div class="ninja" data-ninja='{"components": ["title", "happy", "sad", "stinky", "header", "AnotherComponent"]}'></div>
<div class="mongoose" data-ninja='{"components": ["happy", "sad", "stinky", "angry", "footer"]}'></div>
<div class="other-ninja"></div>
<h1 class="title">Getting React to work like simple jQuery :-D</h1>
<p>Where ALL HTML elements on this page represent React Components. All components being
split into different files and imported using babel es6 compiler</p>
<div class="header">
<div class="angry"></div>
</div>
<div class="content">
<ul>
<li>Just a list showing more component depth</li>
<li>
<div class="someOtherComponent">
<div class="sad"></div>
</div>
</li>
<li>Trying to show the code works regardless of where the elements are</li>
</ul>
<div class="AnotherComponent">
<div class="SomeOtherComponent">
<div class="WhatAnotherComponent"><div class="happy"></div></div>
</div>
</div>
</div>
<div class="footer">
<div class="whatever">
<div class="stinky"></div>
</div>
</div>
SCSS
Removed the SCSS as it was detracting from the main focus of markup and javascript
Script
$(function() {
function Ninja(e, options) {
var $this = $(e);
options = $.extend({}, $this.data('ninja'), options);
var componentList = options.components;
setInterval(function() {
var randomNum = Math.floor(Math.random() * componentList.length);
$this.appendTo('.' + componentList[randomNum]);
}, 1000);
}
$.fn.Ninja = function(options) {
$(this).each(function(i, e) {
Ninja(e, options);
});
};
$('[data-ninja]').each(function(i, e) {
$(e).Ninja();
});
});
$(function() {
$('.other-ninja').Ninja({
components: ['title', 'header', 'angry']
});
});
React might seem like "nightmare" when you used to use jQuery but actually using React is really easy. You just need to adjust your coding logic to suit React guidelines.
The code you shared can be implemented in React many different ways. I did a small working version for you to compare. At first-look it might seem a lot more code to achieve same simple affect but React has a lot more control over the DOM (Virtual DOM) and can be manipulated a lot more different ways. Most of the code in this example is same for all components. You just need to put consideration render method. Appending or removing a component/element can be achieved by just a simple if statement. Also component logic gives you a big flexibility and re-usability. There are a lot of good examples and tutorials online that can show you how you can solve thing in react way. A really good start point is React Docs and awesome-react.
import React from 'react';
import { render } from 'react-dom';
const styles = {
component: {
position: 'absolute',
width: 100,
height: 100
},
happy: {
position: 'absolute',
width: 100,
height: 100,
top: 100,
left: 40,
backgroundColor: 'yellow'
},
sad: {
position: 'absolute',
width: 100,
height: 100,
top: 20,
left: 180,
backgroundColor: 'blue'
},
angry: {
position: 'absolute',
width: 100,
height: 100,
top: 160,
left: 500,
backgroundColor: 'red',
},
stinky: {
position: 'absolute',
width: 100,
height: 100,
top: 210,
left: 300,
backgroundColor: 'green'
},
ninja: {
position: 'absolute',
zIndex: 100,
width: 50,
height: 50,
backgroundColor: '#000'
},
deathstar: {
position: 'absolute',
zIndex: 100,
width: 20,
height: 20,
backgroundColor: '#444'
}
};
const Ninja = () => (<div style={styles.ninja}></div>);
const DeathStar = () => (<div style={styles.deathstar}></div>);
class App extends React.Component {
constructor(props) {
super(props);
this.state = {
types: ['happy', 'sad', 'angry', 'stinky'],
ninja: 0,
deathstar: 0
};
this.intervalNinja = null;
this.intervalDeathstart = null;
}
randomNumber() {
return Math.floor(Math.random() * (3 - 0 + 1)) + 0;
}
componentWillUnmount() {
clearInterval(this.intervalNinja);
clearInterval(this.intervalDeathstart);
this.intervalNinja = null;
this.intervalDeathstart = null;
}
componentDidMount() {
this.intervalNinja = setInterval(() => {
this.setState({
ninja: this.randomNumber()
});
}, 1082);
this.intervalDeathstart = setInterval(() => {
this.setState({
deathstar: this.randomNumber()
});
}, 987);
}
render() {
return(
<div>
{this.state.types.map((type, index) => (
<div style={styles[type]}>
{this.state.ninja === index && <Ninja />}
{this.state.deathstar === index && <DeathStar />}
</div>
))}
</div>
)
}
}
render(<App />, document.getElementById('root'));

Categories

Resources