A small interesting program in Reactjs having issues - javascript

I want to take the numbers from 1 to 12 & want to show them by 4 items in the component like - first 4 numbers with the checkboxes at first, after clicking on next button next 4 numbers with the checkboxes should be showing up with and the previous button comes up to go back ... like this.. & also the even - odd buttons should be showing up beside them & suppose I'm checking on one checkbox in the component when I'm clicking on next and again coming back there, that checkbox should be showing selected or checked.
There have some problem in the code that's why what I'm wanting is not happening. Any help ?
Click here to see the - DEMO
import React, { Component } from 'react';
import { render } from 'react-dom';
import Hello from './Hello';
import MuiThemeProvider from 'material-ui/styles/MuiThemeProvider';
import Checkbox from 'material-ui/Checkbox';
class App extends Component {
constructor() {
super();
this.state = {
ids:[1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12],
getids:[],
idstaken:[],
isPrevious: true,
isNext: false,
counter:0
}
this.getElements = this.getElements.bind(this);
this.handleCheck = this.handleCheck.bind(this);
this.handleDetails = this.handleDetails.bind(this);
this.handlePrevious = this.handlePrevious.bind(this);
this.handleNext = this.handleNext.bind(this);
}
componentDidMount() {
let arr = [];
for(var i=0; i<4; i++) {
arr.push(this.state.ids[i]);
}
this.setState({getids: arr});
}
handlePrevious() {
let arr = [];
if(this.state.counter == 8) {
this.setState({isPrevious: true, isNext: false});
}
for( var i = (this.state.counter - 8); i < (this.state.counter - 4); i++){
arr.push(this.state.ids[i]);
}
this.setState({counter: arr.length, getids: arr});
}
handleNext() {
let arr = [];
if(this.state.counter == 8) {
this.setState({isPrevious: false, isNext: true});
}
for(var i = this.state.counter; i < (this.state.counter + 4); i++){
arr.push(this.state.ids[i]);
}
this.setState({counter: arr.length, getids: arr});
}
handleCheck(e) {
this.state.idstaken.push(e.currentTarget.id);
console.log(e.currentTarget.id);
}
handleDetails() {
console.log("even or odd is clicked!");
}
getElements() {
let details;
let array = [];
let temp = [];
this.state.getids.forEach(function(element) {
if(element % 2 == 0) {
details = <button onClick={this.handleDetails}> even </button>;
}
else {
details = <button onClick={this.handleDetails}> odd </button>;
}
temp.push(
<tr>
<td><Checkbox
id={element}
onCheck={this.handleCheck}
/></td>
<td>{element}</td>
<td>{details}</td>
</tr>
);
}, this)
return temp;
}
render() {
return(
<MuiThemeProvider>
<div>
<div>
<table>
<tr>
<td><button onClick = {this.handlePrevious} hidden={this.state.isPrevious}> Previous </button></td>
<td><button onClick = {this.handleNext} hidden={this.state.isNext}> Next </button></td>
</tr>
</table>
</div>
<div>
<table>
{this.getElements()}
</table>
</div>
</div>
</MuiThemeProvider>
);
}
}
render(<App />, document.getElementById('root'));

Related

How would I update the app state in a React App during a recursive function which is imported from another file?

I am building a sorting algorithm visualization app, where some of the sorting algorithms are recursive. For this example I will use merge sort. I would like my sorting functions to be in a separate file for better file structure but am having a hard time updating the state using these imported functions.
Here is my App.js code:
import React, { Component } from 'react';
import P5Wrapper from 'react-p5-wrapper';
import sketch from './sketches/sketch.js';
import DropdownButton from 'react-bootstrap/DropdownButton';
import Dropdown from 'react-bootstrap/Dropdown'
import ReactBootstrapSlider from 'react-bootstrap-slider'
import "bootstrap-slider/dist/css/bootstrap-slider.css"
import './App.css';
var arrayHelper = require('./helpers/array.js');
var mergeHelper = require('./algorithms/merge.js');
class App extends Component {
constructor(){
super();
//this.reset = this.reset.bind(this);
this.shuffle = this.shuffle.bind(this);
this.setAlgo = this.setAlgo.bind(this);
this.setArraySize = this.setArraySize.bind(this);
this.mergeSort = this.mergeSort.bind(this);
this.arraySize = 500;
this.state = {currentArray: undefined, initialArray: undefined, algo: undefined, arraySize: undefined};
}
componentWillMount(){
//let currentArray, initialArray;
let arr = arrayHelper.createRandomArray(this.arraySize);
this.setState({currentArray: arr, initialArray: arr, algo: 'Select Algorithm', arraySize: 500});
}
//Shuffles array to new random array
shuffle(){
this.setState({currentArray: arrayHelper.createRandomArray(this.state.arraySize)})
}
//Sets user-specified search algorithm based on dropdown button onClick
setAlgo(selectedAlgo){
this.setState({algo: selectedAlgo})
}
//Sets the user selected array size
setArraySize(value){
let arr = arrayHelper.createRandomArray(value);
this.setState({currentArray: arr, arraySize: value})
}
//Runs the imported mergeSort function from separate file
mergeSort(){
mergeHelper.mergeSort(this.state.currentArray, 0, this.state.currentArray.length-1);
}
render() {
return (
<header class='App-header'>
<div class='Buttons'>
<button onClick={this.shuffle}>Shuffle Array</button>
<button onClick={this.mergeSort}>Sort</button>
</div>
<div>
<ReactBootstrapSlider
value={this.state.arraySize}
change={event => this.setArraySize(event.target.value)}
//slideStop={this.changeValue}
step={100}
max={1000}
min={100}
orientation="horizontal"
reversed={false}
//disabled="false"
/>
</div>
<div class='Array'>Array Size: {this.state.arraySize}</div>
<div>
<DropdownButton alignRight title={this.state.algo} id="dropdown-menu-align-right">
<Dropdown.Item eventKey='merge'>
<button onClick={() => this.setAlgo('Merge Sort')}>Merge Sort</button>
</Dropdown.Item>
<Dropdown.Item eventKey='bubble'>
<button onClick={() => this.setAlgo('Bubble Sort')}>Bubble Sort</button>
</Dropdown.Item>
<Dropdown.Item eventKey='quick'>
<button onClick={() => this.setAlgo('Quick Sort')}>Quick Sort</button>
</Dropdown.Item>
<Dropdown.Item eventKey='selection'>
<button onClick={() => this.setAlgo('Selection Sort')}>Selection Sort</button>
</Dropdown.Item>
<Dropdown.Item eventKey='insertion'>
<button onClick={() => this.setAlgo('Insertion Sort')}>Insertion Sort</button>
</Dropdown.Item>
</DropdownButton>
</div>
<div class='Grid'>
<P5Wrapper sketch={sketch}
currentArray={this.state.currentArray}
arraySize={this.state.arraySize}
></P5Wrapper>
</div>
</header>
);
}
}
export default App;
Here is my merge helper file code:
//Function to merge two sorted arrays
function merge(array, left, mid, right){
var n1 = mid - left + 1;
var n2 = right - mid;
var leftArray = [];
var rightArray = [];
for(let i=0;i<n1;i++){
leftArray.push(array[left + i]);
}
for(let j=0;j<n2;j++){
rightArray.push(array[mid + 1 + j]);
}
var i=0;
var j=0;
var k=left;
while(i<n1 && j<n2){
if (leftArray[i] <= rightArray[j]){
array[k] = leftArray[i];
i++;
}
else{
array[k] = rightArray[j];
j++;
}
k++;
}
while(i<n1){
array[k] = leftArray[i];
i++;
k++;
}
while(j<n2){
array[k] = rightArray[j];
j++;
k++;
}
}
//Recursively called mergesort function
function mergeSort(array, left, right){
if(left>=right){
return;
}
var mid = Math.floor((left + (right-1))/2);
mergeSort(array, left, mid);
mergeSort(array, mid+1, right);
merge(array, left, mid, right);
}
module.exports = {merge, mergeSort}
The this.state.currentArray must be updated after each call of merge() but cannot be returned each time due to mergeSort() being recursive. Is there a way to pass this.state as a parameter to mergeSort(), updating this parameter in the mergeSort/merge functions themselves?
This is my first question on Stack so please let me know if I have posted too much info or have been to vague. Thanks.

SyntaxError: invalid increment/decrement operand

am trying to create a calculator in react but when i do the calulaculations it gives me that syntax Error. For example when calaculate different numbers day (30 + 23423 - 3428 *5235) it gives me that error. also i cant get my Reset and backspace and decimal to work. Can someone please help
here is my code
import React, { Component } from 'react'
import Button from "./components/Button";
import Input from "./components/Input";
import {buttonData} from "./components/Data"
class App extends Component {
constructor(props) {
super(props)
this.state = {
input: [],
result: 0,
operator: "+"
}
}
calculate = (num1, operator, num2) => {
let result;
// console.log("math");
if(!isNaN(num2)) {
result = eval(num1 + operator + num2)
this.setState({result})
}
}
handleClick = (button) => {
let input = [...this.state.input]
let result = this.state.result;
let operator = this.state.operator;
const num = parseFloat(input.join(''))
switch(true) {
case /[+-\/\*]/.test(button):
// console.log(operator);
this.calculate(result, operator, num)
operator = button;
input = operator;
break
case/\d/.test(button):
// console.log(button);
if(/[+-\/\*]/.test(button)){
input = []
}
input.push(button)
break
}
if(button === "="){
this.calculate(result, operator, num)
}
else if(button === "C"){
this.reset()
}
else if(button === "CE"){
this.backspace()
}
else if(button === "."){
this.handleDecimal()
}
this.setState({input, operator})
};
reset = () => {
this.setState({
input: "0",
reset: []
})
};
backspace = () => {
this.setState({
input: this.state.input.slice(0, -1)
})
};
handleDecimal = () => {
const { input } = this.state;
// ! add the point only if no point is already included
if(!/\./.test(input)) {
this.setState({input: `${input}.`})
}
}
render() {
return (
<div className="app">
<div className="calc-wrapper">
<h2>Tony's Calculator</h2>
<div id="display" >
<Input input={this.state.input}
result={this.state.result}
/>
</div>
{buttonData.map((item) => {
let name = item.button;
let id = item.id;
return <Button
key={id}
name={name}
id={id}
value={name}
handleClick={this.handleClick}
/>
} )
}
</div>
</div>
)
}
}
export default App;
<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>
enter link description here
The error happens when you try to use eval() or alert() with a mathematical expression that contains -- or ++
Ex:
alert(-50--30.25*32)
will throw: Uncaught SyntaxError: invalid increment/decrement operand
So, you need to surround your value by parenthesis or replace your signs with a single sign.
Ex:
alert(-50-(-30.25)*32)
Or:
str.replace(/[-]{2}|[+]{2}/g, '+')
I would use parenthesis if I was constructing a string and regex if I was reading a string.

Checkbox default checked state issue

I am having the following code. There's some issue I'm facing right now with this thing in my project. I want to use the forEach() loop there inside getElements() instead of map() and also I want to simply show it default checked whenever after checking on a checkbox, going next and again returning back there.
Any help with this issue ??
here's the => DEMO
import React, { Component } from 'react';
import { render } from 'react-dom';
import MuiThemeProvider from 'material-ui/styles/MuiThemeProvider';
import Checkbox from 'material-ui/Checkbox';
class App extends Component {
itemsPerPage = 4
constructor(props) {
super(props);
var ids = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12];
this.state = {
ids: ids,
idsChecked: ids.map(() => false),
page: 0
}
}
componentDidMount = () => {
}
handlePrevious = () => {
this.setState({ page: this.state.page - 1 });
}
handleNext = () => {
this.setState({ page: this.state.page + 1 });
}
handleCheck = (e) => {
var id = Number(e.currentTarget.id);
var idsChecked = this.state.idsChecked.map((bool, i) => i === id ? !bool : bool);
this.setState({ idsChecked: idsChecked });
}
handleDetails = (e) => {
var id = Number(e.currentTarget.getAttribute("rel"));
console.log("even or odd is clicked! (button #id: " + id + ")");
}
getElements = () => {
var first = this.state.page * this.itemsPerPage;
var trs = this.state.ids.slice(first, first + this.itemsPerPage).map((element, i) => {
let details = <button rel={first + i} onClick={this.handleDetails}> {element % 2 ? "odd" : "even"} </button>;
return (
<tr key={element}>
<td><Checkbox
checked={this.state.idsChecked[first + i]}
id={first + i}
onCheck={this.handleCheck}
/></td>
<td>{element}</td>
<td>{details}</td>
</tr>
);
});
return trs;
}
render() {
var hasPrevious = this.state.page > 0;
var hasNext = this.state.page < Math.floor((this.state.ids.length - 1) / this.itemsPerPage);
var tdStyle = {width: "80px"}
return (
<div>
<div>
<table>
<tbody>
<tr>
<td style={tdStyle}>{hasPrevious && <button onClick={this.handlePrevious} hidden={this.state.hasPrevious}> Previous </button>}</td>
<td style={tdStyle}>{hasNext && <button onClick={this.handleNext} hidden={this.state.isNext}> Next </button>}</td>
</tr>
</tbody>
</table>
</div>
<div>
<table>
<tbody>
{this.getElements()}
</tbody>
</table>
</div>
</div>
);
}
}
render(<MuiThemeProvider><App /></MuiThemeProvider>, document.getElementById('root'));
To replace, map with forEach, push the checkbox elements onto an array, and return that array from your getElements().
Use the defaultChecked props of the <Checkbox> component to set the default value to true.
Full code:
getElements = () => {
var first = this.state.page * this.itemsPerPage;
let checkboxArray = []; // array for storing the elements
this.state.ids.slice(first, first + this.itemsPerPage).forEach((element, i) => {
let details = <button rel={first + i} onClick={this.handleDetails}> {element % 2 ? "odd" : "even"} </button>;
checkboxArray.push(
<tr key={element}>
<td><Checkbox
checked={this.state.idsChecked[first + i]}
id={first + i}
defaultChecked={true/*use the defaultChecked prop*/}
onCheck={this.handleCheck}
/></td>
<td>{element}</td>
<td>{details}</td>
</tr>
);
});
return checkboxArray; // return the array
}
render() {
var hasPrevious = this.state.page > 0;
var hasNext = this.state.page < Math.floor((this.state.ids.length - 1) / this.itemsPerPage);
var tdStyle = {width: "80px"}
return (
<div>
<div>
<table>
<tbody>
<tr>
<td style={tdStyle}>{hasPrevious && <button onClick={this.handlePrevious} hidden={this.state.hasPrevious}> Previous </button>}</td>
<td style={tdStyle}>{hasNext && <button onClick={this.handleNext} hidden={this.state.isNext}> Next </button>}</td>
</tr>
</tbody>
</table>
</div>
<div>
<table>
<tbody>
{this.getElements()}
</tbody>
</table>
</div>
</div>
);
}
}

Javascript function not rendering within form

I'm trying to render a select dropdown menu with years.
I'm using a simple loop to generates all the years for the dropdown menu, see dateYear().
If I place {this.dateYear()} outside of {this.state.careerHistoryPositions.map((careerHistoryPosition) it renders correctly however when I place it inside {this.state.careerHistoryPositions.map((careerHistoryPosition) it renders the select element however the years don't populate.
I'm not getting any errors in console either.
export default class CareerHistoryFormPage extends Component {
constructor(props) {
super(props);
const profileCandidateCollection = props.profileCandidate;
const careerHistoryPositions = profileCandidateCollection && profileCandidateCollection.careerHistoryPositions;
this.state = {
careerHistoryPositions: [{company: '', startDateYear: ''}],
};
}
dateYear() {
var yearDate = '';
for (var i = new Date().getFullYear(); i >= 1975; i--) {
yearDate += '<option value="' + i + '">' + i + '</option>';
}
$('select').html('<option>Year</option>' + yearDate);
}
}
render() {
return (
<form onSubmit={this.handleFormSubmit}>
{this.state.careerHistoryPositions.map((careerHistoryPosition) => (
<div key={careerHistoryPosition.uniqueId}>
<input
type="text"
value={careerHistoryPosition.company}
onChange={this.handleCompanyNameChange(careerHistoryPosition.uniqueId)}
/>
<select value={CareerHistoryFormPage.startDateYear} >
{this.dateYear()}
</select>
</div>
</form>
);
}
}
I don't think this is the most elegant solution, however, it's how I got it working. The problem was jquery. Thanks to #nem035 for pointing that out.
export default class CareerHistoryFormPage extends Component {
constructor(props) {
super(props);
const profileCandidateCollection = props.profileCandidate;
const careerHistoryPositions = profileCandidateCollection && profileCandidateCollection.careerHistoryPositions;
this.state = {
careerHistoryPositions: [{company: '', startDateYear: ''}],
};
}
getStartDateMonthSelect(careerHistoryPosition) {
const monthRange = [];
for (let i = 0; i <= 11; i++) {
monthRange.push(i);
}
return (
<select value={careerHistoryPosition.startDateMonth} onChange={this.handleStartDateMonthChange(careerHistoryPosition.uniqueId)}>
<option>Month</option>
{monthRange.map(month => (
<option key={month} value={month}>{moment().month(month).format('MMMM')}</option>
))}
</select>
);
}
}
render() {
return (
<form onSubmit={this.handleFormSubmit}>
{this.state.careerHistoryPositions.map((careerHistoryPosition) => (
<div key={careerHistoryPosition.uniqueId}>
<input
type="text"
value={careerHistoryPosition.company}
onChange={this.handleCompanyNameChange(careerHistoryPosition.uniqueId)}
/>
{this.getStartDateMonthSelect(careerHistoryPosition)}
</div>
</form>
);
}
}

React - Create nested components with loops

I have a little issue with React. I can't create a nested component with a for loop. What I want to do is create 9 cells of a table and then create 3 rows with 3 cells for every row and after that mount the 3 rows together and create a board 9x9.
Let say that I want to get something like this, but using a loop
class Board extends React.Component {
renderSquare(i) {
return <Square value={this.props.squares[i]} onClick={() => this.props.onClick(i)} />;
}
render(){
return(
<div>
<div className="board-row">
{this.renderSquare(0)}
{this.renderSquare(1)}
{this.renderSquare(2)}
</div>
<div className="board-row">
{this.renderSquare(3)}
{this.renderSquare(4)}
{this.renderSquare(5)}
</div>
<div className="board-row">
{this.renderSquare(6)}
{this.renderSquare(7)}
{this.renderSquare(8)}
</div>
</div>
);
}
}
I searched others question for hours and I think my code is almost correct but it does not render what I want. I only get a white page.
here is my code:
class Board extends React.Component {
renderSquare(i) {
return <Square value={this.props.squares[i]} onClick={() => this.props.onClick(i)} />;
}
createCells(i){
if(i%3){return;}
var index = this.fillN(Array(i)); //index=[0,1,2,3,4,5,6,7,8]
var cells = [];
index.forEach(function(i){
cells.push(() => {
return(
<div>
{this.renderSquare(i)}
</div>
);
});
});
return cells;
}
createRows(cells){
var index = this.fillMod3(Array(3)); //index=[0,3,6]
var rows = []
index.forEach(function(i){
rows.push(() => {
return(
<div>
{cells[i]}
{cells[i+1]}
{cells[i+2]}
</div>
);
});
});
return rows;
}
render(){
var cells = this.createCells(9);
var rows = this.createRows(cells);
var board = [];
var index = this.fillN(Array(1));
index.forEach(function(row){
board.push(() => {
return(
<div>{row}</div>
);
});
})
return(
<div>
{board[0]}
</div>
);
}
I always get on the screen something like this:
<Board>
<div> /*empty*/ </div>
</Board>
I want to clarify that I am sure that the rest of the code with which that component (Board) interacts has no issues.
I am new in react and if someoane can help me i will apreciate very much.
Sorry for my poor English
EDIT1:
following marklew examples i should be able to do something like this
render(){
var index1 = this.fillN(Array(3)); //index1=[0,1,2]
var index2 = this.fillN(Array(3)); //index2=[0,1,2]
return(
<div>
{index1.map((e1,i1) => {
return(
<div key={i1} className="board-row">
{index2.map((e2, i2) => {
return(
<p key={i2+10}>
{this.renderSquare(i2)}
</p>
)
})}
</div>
)
})}
</div>
);
}
but it doesn't do what I want. I obtain just a column with 9 cells and the cells are the same objects. I dont understand why. (I understand that are the same objects because i assign a handle function onClick when I create them like that:
<Board
onClick={(i) => this.handleClick(i)} //handleClick just draws a X in the cell
/>
and I get the X drown in 3 cells simultaneously
EDIT2:
I reached a solution:
render(){
var index1 = this.fillMod3(Array(3));
return(
<div>
{index1.map((e,i) => {
return(
<div key={i} className="board-row">
{this.renderSquare(e)}
{this.renderSquare(e+1)}
{this.renderSquare(e+2)}
</div>
)
})}
</div>
);
}
}
but is not what I want. I want another loop even for the intern renderSquare(i) function.
To render a list of elements inside JSX, you can do something like that:
render() {
return <div>
{
[1,2,3].map ( (n) => {
return this.renderSquare(n)
})
}
</div>;
}
Just wrap your array of components into {} in your JSX.
To clarify a bit, this is the same logic of:
return <div>
{
[
<h1 key="1">Hello 1</h1>,
<h1 key="2">Hello 2</h1>,
<h1 key="3">Hello 3</h1>
]
}
</div>;
Note that everytime you render an array of components, you must provide a key prop, as pointed here.
Also, if you want simply print row value in your render function, you should replace:
index.forEach(function(row){
board.push(() => {
return(
<div>{row}</div>
);
});
})
with:
index.forEach( (row, index) => {
board.push(<div key={index}>{row}</div>)
})
or, yet, replacing forEach and push with map:
board = index.map( (row, index) => <div key={index}>{row}</div> )
EDIT I created a fiddle with a 9x9 board using your code as a base: https://jsfiddle.net/mrlew/cLbyyL27/ (you can click on the cell to select it)
I see you too are doing the JS React tutorial! Here's what I did, but I'm working on this because there has to be a good way to give each of these individual keys.
I ran into the same issue you were running into where the X's were drawn into 3 squares, and the reason that happens is because when you were rendering squares, some of the "i's" were duplicated. So there were multiple Squares with the "i" of 2 for example (at least that was the case in my issue).
So each Square has an index right? [0,1,2,3,4,5,6,7,8].
First we need to find how these are related in a way that we can render them into rows! So, in your example you have index1 and index2, which I assume will refer to the x and y coordinates of the Square in the Board. Re-writing the array above we come to: [{0,0}, {1,0}, {2,0}, {0,1}, {1,1}, {2,1}, {0,2}, {1,2}, {2,2}] using {x,y} format. How can we use these values (which we would get from your index1 and index2 in order to get the original array of values that we started with [0,1,2,3,4,5,6,7,8]?
What I did was 3 * x + y (or in a loop, 3 * i + j). This way each square has a unique "i" value associated with it.
After I set up my loops I used this SO post to correctly return the elements I created from a separate function (in a poor attempt to keep my code cleaner).
This is what I ended up with, but I need to make sure to set the keys correctly which is actually how I stumbled onto this post:
createSquares() {
let rows = [];
for(var i = 0; i < 3; i++){
let squares = [];
for(var j = 0; j < 3; j++){
squares.push(this.renderSquare(3*i+j));
}
rows.push(<div className="board-row">{squares}</div>);
}
return rows;
}
Then my render function in Board looks like this:
render() {
return (
<div>
{this.createSquares()}
</div>
);
}
Here is the best I could think of after reading answers here and in Loop inside React JSX
:
render() {
return (
<div>
{
[...Array(3)].map((_, i) => (
<div key={i} className="board-row">
{
[...Array(3)].map((_, j) => this.renderSquare(3 * i + j))
}
</div>
))
}
</div>
);
}
P.S. I'm also doing the challenges in the new React Tutorial. =p
Live demo on codepen
Nested loops in render() function
(explanation in comments:)
class Board extends React.Component {
renderSquare(i) {
return (
<Square
value={this.props.squares[i]}
key={i}
onClick={() => this.props.onClick(i)}
/>
);
}
render() {
var self = this;
return (
<div>
// you can use: [...Array(3)] instead of [1,2,3]
{[1, 2, 3].map(function(row, rowIdx) { // create rows
return (
<div className="board-row" key={rowIdx}>
{
// you can use: [...Array(3)] instead of [1,2,3]
[1, 2, 3].map((col, colIdx) => { // create columns
return self.renderSquare((3 * rowIdx) + colIdx); // calculate square index
})
}
</div>
);
})}
</div>
);
}
}
I will assume you are looking at the React Tutorial example.. As the instructions explicitly point out, the idea is to make two loops, not just the one.
The first loop in this example creates the 3 rows. The second nested loop creates the 3 necessary columns, returning a Square for each position through the renderSquare function. You may notice that I use the indexes of both loops to correctly assign the corresponding index to the square.
return (
<div>
{[0,1,2].map(i => {
return (
<div className="board-row">
{[0,1,2].map(j => {
return this.renderSquare(3*i + j)
})}
</div>
);
})}
</div>
);
Here's my solution. It may help.
renderSquare(i) {
return (
<Square
key={i}
value={this.props.squares[i]}
onClick={() => this.props.onClick(i)}
/>
);
}
render() {
let rows = [];
for (let i = 0; i <= 2; i++) {
let children = []
for (let j = i * 3; j <= i * 3 + 2; j++) {
children.push(this.renderSquare(j))
}
rows.push(
<div key={i} className="board-row">
{children}
</div>
)
}
return (
<div>
{rows}
</div>
);
}
This snippet uses map in a way similar to how we define a nested loop.
const buildBoard = [0, 1, 2].map((row) => {
return (
<div className='board-row' key={row}>
{[0, 1, 2].map((col) => {
return this.renderSquare(row * 3 + col);
})}
</div>
);
});
return <div id='Board Container'>{buildBoard}</div>;
You are pushing functions to the board array. If you want render them, you have to call those functions like
{board[0]()}
I prepared an example that covers your problem: http://jsbin.com/hofohiy/edit?js,output
I'm working on the React tutorial also. Thanks for your responses. I got to this solution:
render() {
const rows = [];
for(let i = 0; i<9; i=i+3) {
const oneRow = [];
for(let j = i; j < i+3; j++) {
oneRow.push(this.renderSquare(j, j))
}
rows.push(<div className="board-row" key={i + 'someId'}>{oneRow}</div>)
}
return (
<div>
{rows}
</div>
);
}
Where I changed renderSquare to set a key for the Square component, as the second argument.
renderSquare(i, key) {
return (
<Square
value={this.props.squares[i]}
onClick={() => this.props.onClick(i)}
key={key}
/>
);
}
This should resolve the problem your facing.
This adds another loop even for the intern renderSquare(i) function.
It does what you want i.e. display 3 columns with 3 cells for the tic tac toe game.
render() {
let count = 0;
const index = [1, 2, 3];
return (
<div>
{index.map((v, i) => {
return (
<div key={i} className="board-row">
{index.map((v2, i2) => {
return this.renderSquare(count++);
})}
</div>
);
})}
</div>
);
}
The first solution:
import React from 'react';
import Square from './Square';
export default
class Board extends React.Component {
render() {
const board2 = [];
for (let i = 0; i < 3; i++) {
const s = [];
for (let k = 0; k < 3; k++) {
s[k] = <Square
key ={3*i+k}
value ={this.props.squares[3*i+k]}
onClick ={()=>this.props.onClick(3*i+k)}
/>;
}
board2[i] = <div className="board-row" key={i}>{s}</div>;
}
///////////////////////////////////////
return (
<div>{board2}</div>
);
}
}
The second solution:
import React from 'react';
import Square from './Square';
export default
class Board extends React.Component {
render() {
let board =Array(3).fill(null);
let square =Array(3).fill(null);
const x = board.map((b,bIndex)=>{
return<div className="board-row" key={bIndex}>
{
square.map((s, sIndex)=>{
return <Square
key ={3*bIndex+sIndex}
value ={this.props.squares[3*bIndex+sIndex]}
onClick ={()=>this.props.onClick(3*bIndex+sIndex)}
/>;
})
}
</div>;
});
///////////////////////////////////////
return (
<div>{x}</div>
);
}
}
Square:
import React from 'react';
export default
function Square(props) {
console.log('square render')
return (
<button className="square" onClick={props.onClick}>
{props.value}
</button>
);
}
class Board extends React.Component {
renderSquare(i) {
return (<Square key={i} value={this.props.squares[i]} onClick={() => this.props.onClick(i)} />);
}
render() {
let board = [];
for(let x = 0; x<3; x++){
let lis =[];
for(let y = 0; y<3; y++){
lis.push(this.renderSquare(3*x + y));
}
board.push(<div key={x}>{lis}</div>)
}
return (
<div>
{board}
</div>
);
}
}
My solution is:
class Board extends React.Component {
//range is a Sequence generator function
//Array.from(arrayLike[, mapFn [, thisArg]]).
//arrayLike - An array-like or iterable object to convert to an array
//mapFn - Map function to call on every element of the array
//thisArg - Value to use as this when executing mapFn
//range(0, 4, 1); => [0, 1, 2, 3, 4]
range = (start, stop, step) => Array.from(
{ length: (stop - start)/step +1 },
(_, i) => start + (i * step)
);
renderSquare(i) {
return <Square
key={i}
value={this.props.squares[i]}
onClickChange={() => this.props.onClickChange(i)}
/>;
}
render() {
let row = 3;
let col = 3;
return (
<div>
{
//This generates an Array of [0, 3, 6] if row = 3 and col = 3
// n is the element and i is the index of the element, i starts at 0
this.range(0, row * col - 1, col).map( (n, i) => {
return (
<div key={i} className="board-row">
{
this.range(n, (i + 1) * col - 1, 1).map( (n, i) => {
return this.renderSquare(n)
})
}
</div>
)
})
}
</div>
);
}
Changed the code of the Board from the tutorial to reflect as
class Board extends React.Component {
constructor(props) {
super(props);
this.rows = Array(3).fill(null);
}
renderSquare(i) {
return (<Square
key={i}
value={this.props.squares[i]}
onClick={() => this.props.onClick(i)}
/>);
}
renderRow(row) {
return (
<div key={row} className="board-row">
{this.rows.map((x, col) => this.renderSquare(3*row+col))}
</div>
);
}
render() {
return (
<div>
{this.rows.map((x, rowIndex) => this.renderRow(rowIndex))}
</div>
);
}
}
The answers above that provide an array, defeat the purpose of a loop; this isn't pretty, but then again, neither is React with JSX:
class Board extends React.Component {
render() {
let board=[];
for(let row=0;row<9;row+=3) {
let cols=[];
for(let col=0;col<3;col++) {
let i=col+row;
cols.push(<Square value={this.props.squares[i]} onClick={() => this.props.onClick(i)}/>)
}
board.push(
<div className="board-row">
{cols}
</div>
);
}
return (
<div>
{board}
</div>
);
}
}
hmm, i think it's way simpler nowadays, cuz i just did
export default function Board(props){
return(
<div id = "board">
{createBoard(3, 3)}
</div>
)
}
function Square(props){
return(
<div className = "boardSquare" style = {{
gridColumn: props.x,
gridRow: props.y}}>
</div>
)
}
function createBoard(x, y){
// x and y being the max "cells" my board will have
let grid = [];
for(let i = 0; i < x; i++){
for(let u = 0; u < y; u++){
grid.push(<Square x = {i + 1} y = {u + 1} key = {`x${i+1}y${u+1}`}/>);
}
}
return grid;
}
and it did exactly what i wanted it to, so yeah, just returning an array solved my problem, i hope my answer is useful to someone.
Just for giggles here's a recent working example as a function component
import React from "react";
import Square from "./Square";
import "./Board.css";
export default function Board(props) {
function renderSquare(i) {
return (
<Square
key={i}
value={props.squares[i]}
onClick={() => props.onClick(i)}
/>
);
}
return (
<div className="board-wrapper">
{
[...Array(3).keys()].map( row => (
<div key={row} className="board-row">
{
[...Array(3).keys()].map( col => renderSquare((row * 3) + col))
}
</div>
)
)
}
</div>
)
}

Categories