React nested loop bug - reactjs

I am trying to build on the beginner react tutorial.
The below code compiles successfully in terminal. Note, only the lines from "Square" downwards are directly relevant, I think.
index.js:
import React from 'react';
import ReactDOM from 'react-dom';
import './index.css';
const size = 30
class DropDown extends React.Component{
algorithm_states = {
alg:0,
}
algClick(i){
this.algorithm_states.alg = i;
}
startResetClick(i){
switch(i){
default:
case 0:
//enable disable buttons on grid
//run relevent processes bases on algorithm state machine
break;
case 1:
// clear grid and matrix
// enable buttons on grid
break;
}
}
render(){
return(
<header>
<button className = "main">
Main
</button>
<div className="dropdown">
<span>Choose Rule</span>
<div className="dropdown-content">
{/* algorithms mapped to their indicies in list */}
<button
onClick={() => this.algClick(0)}
>Game Of Life</button>
<button
onClick={() => this.algClick(1)}
>Langtons Ant</button>
</div>
</div>
{/* start button mapped to 0, reset button mapped to 1 */}
<button
className = "start"
onClick={() => this.startResetClick(0)}
>START</button>
<button
className = "stop"
onClick={() => this.startResetClick(1)}
>RESET</button>
</header>
)
}
}
class Square extends React.Component {
keys = {
x:null,
y:null,
}
render() {
return (
<button className="square" key = {this.keys.x + this.keys.y}>
</button>
);
}
}
class Board extends React.Component{
renderSquare(x, y) {
return (
<Square
keys = {[x,y]}/>
);
}
render(){
const rows = [], cols = [];
for(var i = 0; i < size; i++){
rows[i] = i;
cols[i] = i;
}
return(
<div>
{[...rows].map((x) => {
return (
<div className="board-row" key={x}>
{[...cols].map((y) => this.renderSquare(x,y))}
</div>
)
})
}
</div>
)
}
}
class Game extends React.Component {
render() {
return (
<div className="game">
<header className = "header">
<DropDown/>
</header>
<body>
<div className="game-board">
<Board />
</div>
</body>
</div>
);
}
}
// ========================================
ReactDOM.render(
<Game />,
document.getElementById('root')
);
index.css
body {
font: 14px "Century Gothic", Futura, sans-serif;
margin: 20px;
}
ol, ul {
padding-left: 30px;
}
.header{
background: "black";
}
.main {
position: absolute;
left: 5%
}
.game-board{
position: absolute;
top: 20%;
left:30%;
}
.board-row:after {
clear: both;
content: "";
display: table;
}
.status {
margin-bottom: 10px;
}
.square {
background: #fff;
border: 1px solid #999;
float: left;
font-size: 10px;
font-weight: bold;
line-height: 15px;
height: 15px;
margin-right: -1px;
margin-top: -1px;
padding: 0;
text-align: center;
width: 15px;
}
.square:focus {
outline: none;
}
.kbd-navigation .square:focus {
background: #ddd;
}
.game {
display: flex;
flex-direction: row;
}
.game-info {
margin-left: 20px;
}
.dropdown {
position: absolute;
top: 20%;
left: 5%;
display: inline-block;
}
.dropdown-content {
display: none;
position: absolute;
background-color: #f9f9f9;
min-width: 160px;
box-shadow: 0px 8px 16px 0px rgba(0,0,0,0.2);
padding: 12px 16px;
z-index: 1;
}
.dropdown:hover .dropdown-content {
display: block;
}
.start{
position: absolute;
right: 15%;
}
.stop{
position: absolute;
right: 5%;
}
But in browser, I get the error
Warning: Each child in a list should have a unique "key" prop.
Check the render method of `Board`. See https://reactjs.org/link/warning-keys for more information.
Square#http://localhost:3000/static/js/main.chunk.js:249:5
Board#http://localhost:3000/static/js/main.chunk.js:268:1
div
body
div
Game#http://localhost:3000/static/js/main.chunk.js:308:1
And I'm not sure what it's about, since I'm pretty sure all the keys I have provided are unique.
Anyone got any ideas???
Cheers
K

You need to render each Square component with a key at the map level:
render(){
const rows = [], cols = [];
for(var i = 0; i < size; i++){
rows[i] = i;
cols[i] = i;
}
return(
<div>
{[...rows].map((x) => {
return (
<div className="board-row">
{[...cols].map((y) => (
<Square keys={[x,y]} key={`x-y`} />
))}
</div>
)
})
}
</div>
)
}
}
See the reference on the docs

Related

Input checkbox didn't work what I expected

I tried to make an input checkbox when I click the input checkbox, it should be displayed a check image like this.
However, it didn't show the checkbox and I am not sure how to check that the input box was checked or not. Could you help me with what part do I missed and where is something wrong?
I really appreciate your help!
This is CSS inputl and label part
.colors {
display: flex;
flex-direction: column;
span {
margin-bottom: 20px;
}
.colorLists {
margin-left: 10px;
display: flex;
flex-wrap: wrap;
.colorLayout {
display: flex;
flex-direction: column;
position: relative;
width: 33%;
height: 80px;
flex-shrink: 0;
align-items: center;
justify-content: center;
.checkboxLabel {
background-color: beige;
border: 1px solid #ccc;
border-radius: 50%;
cursor: pointer;
height: 28px;
left: 0;
position: absolute;
top: 40;
width: 28px;
&:after {
border: 2px solid #fff;
border-top: none;
border-right: none;
content: '';
height: 6px;
left: 7px;
opacity: 0;
position: absolute;
top: 8px;
transform: rotate(-45deg);
width: 12px;
// opacity: 0.2;
}
}
input[type='checkbox'] {
visibility: hidden;
}
input[type='checkbox']:checked {
& + label {
background-color: beige;
border-color: beige;
&:after {
opacity: 1;
}
}
}
.productColor {
margin-top: 70px;
font-size: 13px;
margin-right: 21px;
}
}
}
}
.sizes {
.HorizontalLine {
margin-top: 25px;
}
.span {
}
.sizeLists {
margin-top: 20px;
margin-bottom: 20px;
button {
margin: 5px;
width: 44px;
height: 32px;
background-color: white;
border: 1px solid silver;
border-radius: 15%;
}
}
}
This is js part
<div className="colors">
<span>색상</span>
<ul className="colorLists">
{COLOR_LISTS.map((color, idx) => {
return (
<li className="colorLayout" key={idx}>
<input type="checkbox" />
<label
className="checkboxLabel"
for="checkbox"
style={{ backgroundColor: color.colorProps }}
/>
<span className="productColor">{color.color_name}</span>
</li>
);
})}
</ul>
</div>
In react you have to set the htmlFor property for the label instead of for.
The value should be the same as the id from the input.
Then you can add a value property for the input which is used for adding/removing the item in the list of selected items.
For this purpose a handleChange function can be defined.
const [selectedItems, setSelectedItems] = useState([]);
function handleChange(e) {
let newSelected = [];
if (selectedItems.includes(e.target.value)) {
newSelected = selectedItems.filter((item) => item !== e.target.value);
} else {
newSelected = [...selectedItems, e.target.value];
}
setSelectedItems(newSelected);
}
return (
<div className="colors">
<span>색상</span>
<ul className="colorLists">
{COLOR_LISTS.map((color, idx) => {
return (
<li className="colorLayout" key={idx}>
<input
onChange={handleChange}
type="checkbox"
id={idx}
value={color.color_name}
checked={selectedItems.includes(color.color_name)}
/>
<label
className="checkboxLabel"
htmlFor={idx}
style={{ backgroundColor: color.colorProps }}
/>
<span className="productColor">{color.color_name}</span>
</li>
);
})}
</ul>
</div>
);
EDIT: Since you are using a class component it can be rewrittenlike this:
export default class CheckboxListComponent extends Component {
constructor(props) {
super(props);
this.state = { selectedItems: [] };
this.handleChange = this.handleChange.bind(this);
}
handleChange(e) {
let newSelected = [];
if (this.state.selectedItems.includes(e.target.value)) {
newSelected = this.state.selectedItems.filter(
(item) => item !== e.target.value
);
} else {
newSelected = [...this.state.selectedItems, e.target.value];
}
this.setState({ selectedItems: newSelected });
}
render() {
return (
<div className="colors">
<span>색상</span>
<ul className="colorLists">
{COLOR_LISTS.map((color, idx) => {
return (
<li className="colorLayout" key={idx}>
<input
onChange={this.handleChange}
type="checkbox"
id={idx}
value={color.color_name}
checked={this.state.selectedItems.includes(color.color_name)}
/>
<label
className="checkboxLabel"
htmlFor={idx}
style={{ backgroundColor: color.colorProps }}
/>
<span className="productColor">{color.color_name}</span>
</li>
);
})}
</ul>
</div>
);
}
}
You must tell react that your input is checked so that your CSS will apply it. selected ids must be kept in a place for future existence check. in the following code, I named this array selecetedIdx.
You also need to add idx on selection(via onChange event handler) or wrap them all in form and add them via extra dom attribute.
class Main extends Component {
// initialize selectedIdx with [] in your state (esp constructor)
// e.g. this.state = {/* rest of state, */ selectedIdx: []}
render() {
return (
{COLOR_LISTS.map((color, idx) => {
return (
// ...
<input
type="checkbox"
checked={selectedIdx.includes(idx)}
onChange={() => this.setState(state => ({
selectedIdx: [...state.selectedIdx, idx]
}))}
/>
// ...
)}
)
}
Your checkbox element needs name and value properties and would normally be a child of the <form> element.

how to handle children events reactjs?

in Plain JS I can say const corners = document.querySelectorAll('.corner') then I can add event listener to it.
My question is
Haw can I do that with react ?
Is it posibale to add on function in the parent <div> and program
it to add event listener for of its Childs
import React, { useState } from 'react'
import '../css/style.css'
function Boxshape() {
return (
<div
className='item'>
<div className='box'>
<div
onMouseOver={() => console.log('mouse over element A')}
className='corner A'></div>
<div className='corner B'></div>
<div className='corner C'></div>
<div className='corner D'></div>
</div>
</div>
)
}
export default Boxshape
this is my css just in case you are carries
.item{
height: 100px;
width: 100px;
position: absolute;
background-color: aquamarine;
}
.corner{
position: absolute;
width: 10px;
height: 10px;
border-radius: 5px;
background-color: black;
z-index: 2;
}
.A{
top:-1px;
left: -1px;
cursor: nw-resize;
}
.B{
top:-1px;
right: -1px;
cursor: ne-resize;
}
.C{
bottom:-1px;
left: -1px;
cursor: sw-resize;
}
.D{
bottom:-1px;
right: -1px;
cursor: se-resize;
}
Yes, you can attach an onmouseover event to the parent and retrieve the targets. Here is a plain HTML snippet, but you can translate this to React.
function logCorner(e) {
const targetClassNames = e.target.className;
if (targetClassNames.includes('corner')) {
console.log(`mouse over ${targetClassNames}`)
}
}
body {
padding:2rem;
}
.item{
height: 500px;
width: 500px;
position: absolute;
background-color: aquamarine;
}
.corner{
position: absolute;
width: 10px;
height: 10px;
border-radius: 5px;
background-color: black;
z-index: 2;
}
.A{
top:0px;
left: 0px;
cursor: nw-resize;
}
.B{
top:0px;
right: 0px;
cursor: ne-resize;
}
.C{
bottom:0px;
left: 0px;
cursor: sw-resize;
}
.D{
bottom:0px;
right: 0px;
cursor: se-resize;
}
<div className='item'>
<div class='box' onmouseover="logCorner(event)">
<div class='corner A'></div>
<div class='corner B'></div>
<div class='corner C'></div>
<div class='corner D'></div>
</div>
</div>
Depending on how complex your corner divs become you can adopt a variety of patterns.
Here is a simple example that abstracts the corner divs to a Handle component
function Boxshape() {
return (
<div className='item'>
<div className='box'>
<Handle className="A" />
<Handle className="B" />
<Handle className="C" />
<Handle className="D" />
</div>
</div>
)
}
function Handle({className}) {
const mouseOverHandler = () => {
console.log(`mouse over: ${className}`)
}
return (
<div
onMouseOver={mouseOverHandler}
className={`corner ${className}`}></div>
)
}
export default Boxshape
Yeah, you can pass in the event in the element tag, and then make a function (you decide the functions name), and then you can pass in the function into the element, by saying this.
don't do () cause we only log it once on click, also we put it in brackets, cause it's a js code, not a react.
function handleClick() {
console.log('Don\'t you dare click me lol')
};
<button onClick={handleClick}>I\'m a button</button>

How to open a multiple message box with ReactJS

I'm building a multiple chatbox messaging like Facebook's popup messenger windows. At the moment, the user can call up a chatbox, close it, minimize it, etc.
When I click on multiple users, say 3, I'm supposed to have three chatboxes pop up corresponding to those three different users. Currently, only one chatbox appears.
This screenshot illustrates what I want to achieve. On each user's button click, it's own chatbox will popup.
Here is the demo and download link for a jQuery equivalent: link.
This is the full React code which shows just one chat box:
import React, { Component, Fragment } from "react";
import { render } from "react-dom";
import axios from 'axios';
import './style.css';
class MessageBox extends Component {
constructor() {
super();
this.state = {
showBox: false,
shownToggle: true,
data: [
{ id: "1", name: "Tony" },
{ id: "2", name: "Mark" },
{ id: "3", name: "Joy" }
],
currentRec: undefined,
};
this.showBox = this.showBox.bind(this);
this.closeBox = this.closeBox.bind(this);
this.toggle = this.toggle.bind(this);
}
showBox = (i, pid, name) => {
this.setState({ currentRec: i });
console.log(`Selected record index: ${i}`);
alert(pid);
alert(name);
this.setState({ showBox: true }, () => {
document.addEventListener('click', this.closeBox);
});
}
closeBox(event) {
if (this.dropdownBox.contains(event.target)) {
this.setState({ showBox: false }, () => {
document.removeEventListener('click', this.closeBox);
});
}
}
toggle() {
this.setState({
shownToggle: !this.state.shownToggle
});
}
render() {
var hidden = {
display: this.state.shownToggle ? "block" : "none"
}
return (
<div>
<ul style={{ float: "right" }}>
{this.state.data.map((person, i) => (
<div className="chat-sidebar" key={i}>
<button onClick={() => this.showBox(i, person.id, person.name)}>Chat with {person.name}</button>
{this.state.showBox ? (
<div className="msg_box" style={{ right: '270px' }}>
<div onClick={this.toggle.bind(this)} class="msg_head">
(<b style={{ color: 'orange' }}>
{this.state.currentRec !== undefined &&
<div className="modal-body">
{this.state.data[this.state.currentRec].name}
({this.state.data[this.state.currentRec].id})
</div>
}
</b>)
Minimize
<div className="close" ref={(element) => { this.dropdownBox = element; }} style={{ color: 'white' }}>Close</div>
</div>
<div style={hidden} className="msg_wrap"><div className="msg_body">Message will appear here</div></div>
</div>) : (null)}
</div>
))}
</ul>
</div>
);
}
}
/****** Chat Popup Layout ******/
body{
background: #e5e5e5;
font-family: sans-serif;
}
.msg_box{
position: fixed;
bottom: -5px;
width: 250px;
background: white;
border-radius: 5px 5px 0px 0px;
}
.msg_head{
background: black;
color: white;
padding: 8px;
font-weight: bold;
cursor: pointer;
border-radius: 5px 5px 0px 0px;
}
.msg_body{
background: white;
height: 200px;
font-size: 12px;
padding: 15px;
overflow: auto;
overflow-x: hidden;
}
.close{
float: right;
cursor: pointer;
}
.minimize{
float: right;
cursor: pointer;
padding-right: 5px;
}
/****** Slider Layout Popup ******/
.chat-sidebar {
width: 250px;
height: 100%;
right: 0px;
top: 0px;
padding-top: 10px;
padding-bottom: 10px;
border: 1px solid #b2b2b2;
}
After series of efforts, the use of jquery and Reactjs is what solved my issue as per code below
const dataSet = this.state.data;
alert(dataSet);
if ($.inArray(pid, dataSet) != -1)
{
dataSet.splice($.inArray(pid, this.state.data), 1);
}
dataSet.unshift(pid);
var s = 270 ; // start position
var j = 260; //next position
$.each(dataSet, function( index, value ) {
if(index < 4){
$('[rel="'+value+'"]').css("right",s);
$('[rel="'+value+'"]').show();
s = s+j;
}
else{
$('[rel="'+value+'"]').hide();
}

React-modal hides behind elements

I am trying to make use of react-modal for the first time. When I click on the sign-in button, the react-modal component is invoke but seems to be hiding behind the cover page which is a video landing page.
The React devtool displays the appropriate states before the sign-in button is clicked
before the sign-in button is clicked
When the sign-in button is now clicked, the react devtool now displays that the ModalPortal component is rendered showing the appropriate states
when the sign-in button is clicked
SignInModal.scss
.ReactModalPortal>div {
opacity: 0;
}
.ReactModalPortal .ReactModal__Overlay {
align-items: center;
display: flex;
justify-content: center;
transition: opacity 200ms ease-in-out;
}
.ReactModalPortal .ReactModal__Overlay--after-open {
opacity: 1;
}
.ReactModalPortal .ReactModal__Overlay--before-close {
opacity: 0;
}
.modal {
position: relative;
background: #464b5e;
color: white;
max-width: 90rem;
outline: none;
padding: 3.2rem;
text-align: center;
}
.modal__title {
margin: 0 0 1.6rem 0;
}
.modal__body {
font-size: 2rem;
font-weight: 300;
margin: 0 0 3.2rem 0;
word-break: break-all;
}
CoverPage.js Component
import Header from './Header';
import HeaderVideo from './HeaderVideo';
import SignInModal from './SignInModal';
import React, { Component } from 'react';
class CoverPage extends Component {
state = {
modalIsOpen: false
};
onOpenModal = () => {
this.setState(() => ({
modalIsOpen: true
}));
};
onCloseModal = () => {
this.setState(() => ({
modalIsOpen: false
}));
};
render() {
return (
<div>
<Header />
<HeaderVideo onOpenModal={this.onOpenModal} />
<SignInModal
modalIsOpen={this.state.modalIsOpen}
onOpenModal={this.onOpenModal}
onCloseModal={this.onCloseModal}
/>
</div>
);
}
}
export default CoverPage;
HeaderVideo.js Component
import React from 'react';
import Signup from './Signup';
import CoverInfo from './CoverInfo';
const HeaderVideo = props => {
return (
<div className="video-container">
<video preload="true" autoPlay loop volume="0" postoer="/images/1.jpg">
<source src="images/vine.mp4" type="video/mp4" />
<source src="images/vine1.webm" type="video/webm" />
</video>
<div className="video-content">
<div className="container content">
<div className="row">
<div className="col-md-9">
<CoverInfo onOpenModal={props.onOpenModal} />
</div>
<div className="col-md-3">
<Signup />
</div>
</div>
</div>
</div>
</div>
);
};
export default HeaderVideo;
CoverInfo.js Component
import React from 'react';
const CoverInfo = props => {
return (
<div className="info">
<div>
<h1>Welcome to EventCity!</h1>
</div>
<div>
<p>
At EventCity! we pride ourselves on the unrivalled personal {`event`} services,we provide
to our clientele. We guide you from the stressful decision making {`process`},ensuring you
are comfortable,whether it is a wedding, corporate {`function `}or even a kiddies party,we
create a buzz around you, taking you to the next level.
</p>
</div>
<div>
<h3>Innovation, {`Performance`} and Delivery</h3>
</div>
<button type="button" className="btn btn-success btn-lg" onClick={props.onOpenModal}>
Sign In here
</button>
</div>
);
};
export default CoverInfo;
video-cover.scss
video {
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
min-width: 100%;
min-height: 100%;
width: auto;
height: auto;
z-index: 1;
}
.video-content {
z-index: 2;
position: absolute;
background: rgba(0, 0, 0, 0.6);
top: 0;
bottom: 0;
left: 0;
right: 0;
}
.content {
padding-top: 120px;
}
You need to set the z-index property on the Modal's overlay, which normally has a z-index of 0. The CSS class is .ReactModal__Overlay
Here is the pure-React way of doing it:
const customStyles = {
content : {
...
},
overlay: {zIndex: 1000}
};
<Modal style={customStyles}>
...
</Modal>
.modal {
position: fixed;
z-index:9999;
top :0;
left:0;
right:0;
bottom:0;
background: #464b5e;
color: white;
outline: none;
padding: 3.2rem;
text-align: center;
}
Example of react-modal inline styles Set the styles in the react-modal inline styles. The z-index to 100 but make just like below
style={{
overlay: {
zIndex: 100,
backgroundColor: 'rgba(70, 70, 70, 0.5)',
},

react-modal is rendered at bottom of the screen

I'm using react-modal in my app, but I can't easily get it to render on top of the current screen content. The modal always renders at the bottom of the screen (below the html body even).
This is my custom modal:
import Modal from 'react-modal';
var SimpleModal = React.createClass({
render() {
return (
<Modal
isOpen={this.props.isOpen}
className="modal-content"
contentLabel="modal"
onRequestClose={this.props.onClose} >
<h1 className="modal-header">{this.props.title}</h1>
<div className="modal-body">
<p>{this.props.message}</p>
</div>
<Button bsStyle={this.props.type} className="modal-button" onClick={this.props.closeModal}>Close</Button>
</Modal>
)
}
});
const mapStateToProps = (state) => {
return {
isOpen: state.modals.notification.isOpen,
type: state.modals.notification.type,
title: state.modals.notification.title,
message: state.modals.notification.message,
}
};
const mapDispatchToProps = (dispatch) => {
return {
closeModal: () => dispatch(skjeraActionCreators.closeNotificationModal()),
}
};
export default connect(mapStateToProps, mapDispatchToProps)(SimpleModal)
The SimpleModal component is included in the render function of my top level container (AppContainer), like this:
render() {
return (
<div>
<SimpleModal />
<App
onLogout={this.logout}
isLoggedIn={this.props.isLoggedIn}
username={this.props.username}
subpages={this.props.children}
/>
</div>
)
}
Note that I haven't tweaked the style/css, so it uses the default styling, and thus the default positioning scheme.
Can anyone help me out tracking down this bug? Any help will be appreciated.
EDIT: This is the CSS entries (probably some redudant elements there) I've referred to in my code:
.modal-header {
background-color: inherit;
border: none;
}
.modal-body {
padding-top: 10px;
padding-bottom: 10px;
}
.modal-button {
padding-left: 10%;
margin-left: 20px;
}
.modal-content {
position: absolute;
background-color: white;
color: black;
top: auto;
bottom: auto;
overflow: auto;
right: auto;
left: 10px;
border-radius: 20px;
outline: none;
border: solid;
border-width: medium;
border-color: black;
padding-bottom: 10px;
}

Resources