I created array of buttons as shown below in the code section. I am using sandbox. what i want to do is, when i click button 3 i should receive in the console
button 3
and so on.
please let me know how should i pass value to onClick callback
code:
import React from "react";
import ReactDOM from "react-dom";
import createReactClass from "create-react-class";
var arrButtons = [];
var buttonStyle = {
margin: "10px 10px 10px 0"
};
class ButtonClicks extends React.Component {
constructor(props) {
super(props);
this.onClickFunction = this.onClickFunction.bind(this);
}
onClickFunction() {
console.log("button ");
console.log(this.props.log);
}
render() {
for (let i = 0; i < 10; i++) {
//Moved your loop outside render()'s return
arrButtons.push(
<button style={buttonStyle} onClick={this.onClickFunction}>
{i}
{this.props.i}
</button>
);
}
return (
<div>
{arrButtons} {/*Very important to wrap the buttons inside a div*/}
</div>
);
}
}
const rootElement = document.getElementById("root");
ReactDOM.render(<ButtonClicks />, rootElement);
export default ButtonClicks;
You can create a new inline function that passes the index to your onClickFunction.
Example
const buttonStyle = {
margin: "10px 10px 10px 0"
};
class ButtonClicks extends React.Component {
constructor(props) {
super(props);
this.onClickFunction = this.onClickFunction.bind(this);
}
onClickFunction(index) {
console.log(`button ${index}`);
}
render() {
const arrButtons = [];
for (let i = 0; i < 10; i++) {
arrButtons.push(
<button style={buttonStyle} onClick={() => this.onClickFunction(i)}>
{i}
{this.props.i}
</button>
);
}
return (
<div>
{arrButtons}
</div>
);
}
}
ReactDOM.render(<ButtonClicks />, document.getElementById("root"));
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react-dom.min.js"></script>
<div id="root"></div>
Just try with creating dynamically an anonymous function that invokes your onClickFunction:
<button style={buttonStyle} onClick={() => this.onClickFunction(i)}>
Now your onClickFunction can take an argument:
onClickFunction(i) {
console.log(`button ${i}`);
}
You can pass the value as params to the callback function, not as a props.
for (let i = 0; i < 10; i++) {
arrButtons.push(
<button style={buttonStyle} onClick={()=>this.onClickFunction(i)}>
{i}
</button>
);
}
pass the value onClick button. and receive in function as below.
onClickFunction(i) {
console.log("button ",i);
console.log('value received as params not as props like this.props.log');
}
Related
Upon clicking the submit button many things happen including cancelling the countdown clock. I am using setTimeout() and it actually works just fine, but for some reason I can no longer delete items. When I remove the setTimeout() deleting the items works just fine. I really have no idea what's going on as they are completely separate things and use completely separate variables.
import React from 'react';
import ReactDOM from 'react-dom';
import './index.css';
import { Image } from './Image.js'
import { Button } from './Button.js'
import { images } from './assets/images.js'
import { Countdown } from './Countdown.js'
import { DisplayCount } from './DisplayCount.js'
import { Inputs } from './Inputs.js'
class Game extends React.Component{
constructor(props){
super(props)
this.timer = null
this.thoughts = []
this.thought = []
this.state = {
currentImg: 0,
timer: null,
ranNum: null,
thought: [],
isSubmitted: false
}
this.handleClick = this.handleClick.bind(this)
this.handleSubmit = this.handleSubmit.bind(this)
this.handleDeleteClick = this.handleDeleteClick.bind(this). < -------------
}
countdownClock = async (newRanNum) => {
const startingNum = newRanNum * 20;
for(let i = startingNum; i >= 0; i--) {
await new Promise(resolve => {
this.timer = setTimeout(() => {
this.setState({
timer: i
})
resolve()
}, 1000)
});
}
}
handleChange(event, index) {
const inputs = [...this.state.thought];
inputs[index] = event.target.value
this.setState({
thought: inputs
});
}
handleClick(){
clearTimeout(this.timer)
let newRanNum = Math.floor(Math.random() * 20);
this.countdownClock(newRanNum)
this.generateStateInputs(newRanNum)
// this.generateInputs(this.state.thought)
let current = this.state.currentImg;
let next = ++current % images.length;
this.setState({
currentImg: next,
ranNum: newRanNum
})
}
generateStateInputs(newRanNum){
let inputArray = []
for(let i = 0; i < newRanNum; i++){
inputArray.push('')
}
return this.setState({
thought: inputArray
});
}
handleSubmit(event) {
if(event){
clearTimeout(this.timer) <------------------------------------------
event.preventDefault();
let thought = this.state.thought.map(word => word + ' ');
console.log(thought)
this.thoughts.push(thought)
event.preventDefault()
this.setState({
thought: []
})
}
}
handleDeleteClick(index){ <-------------------------------------------------------
return this.thoughts = this.thoughts.filter(thought => thought !== this.thoughts[index])
}
render(){
let src = this.state.currentImg;
return(
<div>
<Countdown name={'Countdown: '} countdown={this.state.timer} />
<DisplayCount name='Word Count: ' count={this.state.ranNum} />
<Image src={images[src]} />
<Button onClick={this.handleClick} name='Generate Inputs' />
<form onSubmit={this.handleSubmit}>
<ol>
{this.state.thought.map((input, index) => (
<Inputs type='text' key={index} value={input} onChange={event => { this.handleChange(event, index) }} className='textInputs' />
))}
</ol>
<input type='submit' value='Submit' />
</form>
<div>
<ol>
{this.thoughts.map((thought, index )=>
<DisplayPoem key={index} onClick={() => { this.handleDeleteClick(index) }} name='Delete Thoughts' value={thought} /> <---------------------------------------------
)}
</ol>
</div>
</div>
)
}
}
class DisplayPoem extends React.Component {
render(){
return (
<li>
<span>{this.props.value}</span>
<button onClick={this.props.onClick}>{this.props.name}</button>
</li>
)
}
}
ReactDOM.render(
<Game />,
document.getElementById('root')
);
I am trying to generate inputs on a button click and the amount of inputs is generated by a random number. Here is what I have so far, but it isn't working. I am very confused and feel like it should be working. I am not sure what I am missing. Any help would be very much appreciated.
import React from 'react';
import ReactDOM from 'react-dom';
import './index.css';
import { Image } from './Image.js'
import { Button } from './Button.js'
import { images } from './assets/images.js'
import { Countdown } from './Countdown.js'
import { DisplayCount } from './DisplayCount.js'
import { Inputs } from './Inputs.js'
class Game extends React.Component{
constructor(props){
super(props)
this.timer = null
this.state = {
currentImg: 0,
timer: null,
ranNum: null
}
this.handleClick = this.handleClick.bind(this)
}
countdownClock = async (newRanNum) => {
const startingNum = newRanNum * 20;
for(let i = startingNum; i >= 0; i--) {
await new Promise(resolve => {
this.timer = setTimeout(() => {
this.setState({
timer: i
})
resolve()
}, 1000)
});
}
}
generateInputs = (newRanNum) => {
const inputs = []
for(let i = 1; i <= newRanNum; i++){
inputs.push(
<Inputs type='text' className='textInputs' />
)
}
return inputs;
}
handleClick(){
clearTimeout(this.timer)
let newRanNum = Math.floor(Math.random() * 20);
this.countdownClock(newRanNum)
this.generateInputs(newRanNum)
let current = this.state.currentImg;
let next = ++current % images.length;
this.setState({
currentImg: next,
ranNum: newRanNum
})
}
render(){
let src = this.state.currentImg;
return(
<div>
<Countdown name={'Countdown: '} countdown={this.state.timer} />
<DisplayCount name='Word Count: ' count={this.state.ranNum} />
<Image src={images[src]} />
<Button onClick={this.handleClick} />
<div>
<ul>
{this.generateInputs()}
</ul>
</div>
</div>
)
}
}
ReactDOM.render(
<Game />,
document.getElementById('root')
);
Inputs component:
import React from 'react'
export const Inputs = (props) => {
return (
<li className={props.className}>
<input value={props.value} />
</li>
)
}
I believe the issue is here...
generateInputs = (newRanNum) ...
and here...
{this.generateInputs()}
You're input rendering function is expecting a parameter it's not getting, and since that returns as undefined the loop never runs. :)
In general, it is most common to separate state (eg, number of inputs) from the rendering, and only have the rendering respond to state. Perhaps you had originally intended generateInputs() to produce the array, and not render them (?)
The first thing is your generateInputs() function takes a parameter for number of inputs you wanted to render but you are not passing any parameter to this.generateInputs()
The second thing is you are returning an array from this function but not mapping your array in the render function, So do in this way.
this.generateInputs(10) // pass parameter here
this.generateInputs(10).map((item, index)=>item)
In this Snack, I have done the same thing but with react-native
https://snack.expo.io/#waheed25/smiling-carrot
I was making react version of game Bingo. I added 25 buttons which are the child components.
Initially i have empty values in each button. Then on each click, i was trying to update the clicked button's values from 1 to 25.
But when i click on one button, to update the button label, all button's values are getting updated. Can anyone suggest the reason behind that?
App.js
import React from "react";
import "./styles.css";
import GameContainerOne from "./GameContainerOne";
export default function App() {
return (
<div className="App">
<GameContainerOne />
</div>
);
}
GameContainerOne.js
import React from "react";
import ButtonBox from "./ButtonBox";
class GameContainerOne extends React.Component {
constructor(props) {
super(props);
this.state = {
btnLabel: 0
};
}
handleClicked = () => {
if (this.state.btnLabel < 25) {
this.setState({ btnLabel: ++this.state.btnLabel });
console.log("after", this.state.btnLabel);
}
};
render() {
let menuItems = [];
let key = 0;
for (let i = 0; i < 5; i++) {
for (let j = 0; j < 5; j++) {
let index = "" + i + j;
// console.log(index)
key++;
menuItems.push(
<ButtonBox
key={key}
index={index}
value={this.state.btnLabel}
handleClicked={this.handleClicked.bind(this)}
/>
);
}
}
return <div className="wrapper">{menuItems}</div>;
// this.handleButtonBox()
}
}
export default GameContainerOne;
ButtonBox.js
import React from "react";
import Button from "#material-ui/core/Button";
class ButtonBox extends React.Component {
constructor(props) {
super(props);
this.state = {
initialBtnColor: "Default",
selectedBtnColor: "Primary"
};
}
handleClick = () => {
// console.log("before",this.state.btnLabel)
this.setState({ initialBtnColor: "Primary" });
return this.props.handleClicked;
};
render() {
console.log("Key=", this.props);
// const { index } = this.props.index;
console.log("Key=", this.props.index);
return (
<div>
<Button
variant="contained"
color={this.state.initialBtnColor}
onClick={this.props.handleClicked}
>
{this.props.value}
</Button>
</div>
);
}
}
export default ButtonBox;
Please find the codesandbox link : https://codesandbox.io/s/bingo-game-glk8v
Move the btnLabel state into the ButtonBox.
Example (using hooks):
// ButtonBox.js
import React from "react";
import Button from "#material-ui/core/Button";
function ButtonBox(props) {
const [buttonColor, setButtonColor] = React.useState("Default");
const [value, setValue] = React.useState(props.startValue);
return (
<div>
<Button
variant="contained"
color={buttonColor}
onClick={() => {
setValue(value + 1);
setButtonColor("Primary");
props.handleClicked(props.index);
}}
>
{value}
</Button>
</div>
);
}
export default ButtonBox;
// GameContainerOne.js
import React from "react";
import ButtonBox from "./ButtonBox";
function GameContainerOne(props) {
const handleClicked = React.useCallback(btnIndex => {
// Called after a button is clicked
}, []);
let menuItems = [];
let key = 0;
for (let i = 0; i < 5; i++) {
for (let j = 0; j < 5; j++) {
let index = "" + i + j;
key++;
menuItems.push(
<ButtonBox
key={key}
index={index}
startValue={0}
handleClicked={handleClicked}
/>
);
}
}
return <div className="wrapper">{menuItems}</div>;
}
export default GameContainerOne;
You change the state on click which is the source of labels for all the buttons.
You could have a separate state for all the buttons to avoid that and changed them based on the index on the button clicked.
I created a main component in ReactJs called MainPage (using Material-UI).
import React from 'react';
import Grid from '#material-ui/core/Grid';
import Button from '#material-ui/core/Button';
import CssBaseline from '#material-ui/core/CssBaseline';
import Card from '#material-ui/core/Card';
import CardContent from '#material-ui/core/CardContent';
import withStyles from '#material-ui/core/styles/withStyles';
const styles = theme => ({
card: {
minWidth: 350,
},
button: {
fontSize: '12px',
margin: theme.spacing.unit,
minWidth: 350
},
extendedIcon: {
marginRight: theme.spacing.unit,
}
});
class MainPage extends React.Component {
constructor() {
super();
}
render() {
const {
classes
} = this.props;
return ( <
React.Fragment >
<
CssBaseline / >
<
Grid container spacing = {
0
}
direction = "column"
alignItems = "center"
justify = "center"
style = {
{
minHeight: '100vh'
}
} >
<
form onSubmit = {
this.handleSubmit
} >
<
Card className = {
classes.card
} >
<
CardContent >
<
Grid item xs = {
3
} >
<
Button variant = "contained"
size = "medium"
color = "primary"
className = {
classes.button
}
type = "submit"
value = "single" >
ButtonA <
/Button> <
/Grid> <
Grid item xs = {
3
} >
<
Button variant = "contained"
size = "medium"
color = "primary"
className = {
classes.button
}
type = "submit"
value = "batch" >
ButtonB <
/Button> <
/Grid> <
/CardContent> <
/Card> <
/form> <
/Grid> <
/React.Fragment>
);
}
}
export default withStyles(styles)(MainPage);
I want to load a new component (either CompA or CompB, depending on which button was clicked - ButtonA or ButtonB) on button click. A new component should completely replace a current component - I mean that it should be loaded in a whole screen (not anywhere next to buttons).
How can I do it?
UPDATE:
I want to replace MainPage component, not just render on top of it.
This is how I load MainPage:
index.js
import React from 'react';
import { render } from 'react-dom';
import MainPage from './components/MainPage';
const View = () => (
<div>
<MainPage/>
</div>
);
render(<View />, document.getElementById('root'));
You can create a different component to handle the state and add an if statement in that component to handle the view that you want to render.
You can see the example here codesandbox.io/embed/6wx2rzjrr3
App.js
import React, { Component } from "react";
import ReactDOM from "react-dom";
import Main from "./Main";
import View1 from "./View1";
import View2 from "./View2";
import "./styles.css";
class App extends Component {
state = {
renderView: 0
};
clickBtn = e => {
this.setState({
renderView: +e.target.value
});
};
render() {
switch (this.state.renderView) {
case 1:
return <View1 />;
case 2:
return <View2 />;
default:
return <Main clickBtn={this.clickBtn} />;
}
}
}
const rootElement = document.getElementById("root");
ReactDOM.render(<App />, rootElement);
Main.js
import React from "react";
export default props => (
<>
Main view{" "}
<button value={1} onClick={props.clickBtn}>
View 1
</button>{" "}
<button value={2} onClick={props.clickBtn}>
View 2
</button>{" "}
</>
);
View1.js
import React from "react";
export default props => "View 1";
View2.js
import React from "react";
export default props => "View 2";
In your index.js you can use
const View = () => (
<div>
<MainPage condition='something' />
</div>
);
Then in you main page:
class MainPage extends React.Component {
constructor() {
super();
}
myCondition1() {
return (
<Component1 />
);
}
myCondition2() {
return (
<Component2 />
);
}
render() {
const { condition} = this.props;
return (
{condition === 'something' ? this.myCondition1() : this.myCondition2()}
)
}
}
UPDATE
Here's an example with a simple button:
class Condition1 extends React.Component {
render() {
return (
<div>Condition 1</div>
);
}
}
class Condition2 extends React.Component {
render() {
return (
<div>Condition 2</div>
);
}
}
class App extends React.Component {
constructor(props) {
super(props);
this.state = {
condition: true
};
this.handleClick = this.handleClick.bind(this);
}
handleClick(condition) {
this.setState( {condition} )
}
render() {
const { condition } = this.state;
return (
<div>
<button onClick={() => this.handleClick(true)}>Condition1</button>
<button onClick={() => this.handleClick(false)}>Condition2</button>
{condition === true ? <Condition1 /> : <Condition2 />}
</div>
)
}
}
ReactDOM.render( < App / > ,
document.getElementById('root')
);
<script src="https://unpkg.com/react#16/umd/react.development.js" crossorigin></script>
<script src="https://unpkg.com/react-dom#16/umd/react-dom.development.js" crossorigin></script><div id="root" />
I created array of buttons, let say I have 20 buttons. I have 20 mp3 files as well. Now what i want to do is to link the button callback "onclick" with a
specific .mp3 file. So that, when the button number 5 is clicked, the 5th mp3 will be executed. how can I achieve this task by modifying the below
posted code? please provide an example.
note: the mp3 files are save locally on the hard drive.
please let me know how should i pass value to onClick callback
code:
import React from "react";
import ReactDOM from "react-dom";
import createReactClass from "create-react-class";
var arrButtons = [];
var buttonStyle = {
margin: "10px 10px 10px 0"
};
class ButtonClicks extends React.Component {
constructor(props) {
super(props);
this.onClickFunction = this.onClickFunction.bind(this);
}
onClickFunction() {
console.log("button ");
console.log(this.props.log);
}
render() {
for (let i = 0; i < 10; i++) {
//Moved your loop outside render()'s return
arrButtons.push(
<button style={buttonStyle} onClick={this.onClickFunction}>
{i}
{this.props.i}
</button>
);
}
return (
<div>
{arrButtons} {/*Very important to wrap the buttons inside a div*/}
</div>
);
}
}
const rootElement = document.getElementById("root");
ReactDOM.render(<ButtonClicks />, rootElement);
export default ButtonClicks;
You would benefit from using JS's .map and creating a button that has a unique onClick passing that specific mp3's id back into your onClick function. From there you can lookup by id, then play only that specific file.
import React from "react";
import ReactDOM from "react-dom";
import createReactClass from "create-react-class";
class ButtonClicks extends React.Component {
constructor(props) {
super(props);
this.state = {mp3Files}
this.onClickFunction = this.onClickFunction.bind(this);
}
onClickFunction(mp3UniqueIdToLookup) {
console.log("button ");
console.log(this.props.log);
//Here you would search your files for the mp3 with the corresponding id and then play that one only.
}
render() {
const buttonStyle = {
margin: "10px 10px 10px 0"
};
return (
<div>
{this.state.mp3Files.map((index, ele) => {
return <button style={buttonStyle} key={index} onClick={() => this.onClickFunction(ele.id)}/>
})}
</div>
);
}
}
const rootElement = document.getElementById("root");
ReactDOM.render(<ButtonClicks />, rootElement);
export default ButtonClicks;
Here is my solution. While doing loop you can pass the no to event handler function and access the no. This is how it works when you do looping
ES5 way
import React, {Component} from "react";
import ReactDOM from "react-dom";
import createReactClass from "create-react-class";
var arrButtons = [];
var buttonStyle = {
margin: "10px 10px 10px 0"
};
class ButtonClicks extends Component {
constructor(props) {
super(props);
this.onClickFunction = this.onClickFunction.bind(this);
}
onClickFunction(no) {
console.log("Here you will get button no. if you click button 5 you will get button no 5 and you can pass the same to log");
console.log(this.props.log(no));
}
render() {
for (let i = 1; i <=10; i++) {
arrButtons.push(
<button style={buttonStyle} onClick={this.onClickFunction(i)}>
{i}
{this.props.i}
</button>
);
}
return (
<div>
{arrButtons} {/*Very important to wrap the buttons inside a div*/}
</div>
);
}
}
const rootElement = document.getElementById("root");
ReactDOM.render(<ButtonClicks />, rootElement);
export default ButtonClicks;
ES6 way
import React, {Component} from "react";
import ReactDOM from "react-dom";
import createReactClass from "create-react-class";
var arrButtons = [];
var buttonStyle = {
margin: "10px 10px 10px 0"
};
class ButtonClicks extends Component {
constructor(props) {
super(props);
}
onClickFunction = (no) => {
console.log("Here you will get button no. if you click button 5 you will get button no 5 and you can pass the same to log");
console.log(this.props.log(no));
}
render() {
for (let i = 1; i <=10; i++) {
arrButtons.push(
<button style={buttonStyle} onClick={this.onClickFunction(i)}>
{i}
{this.props.i}
</button>
);
}
return (
<div>
{arrButtons} {/*Very important to wrap the buttons inside a div*/}
</div>
);
}
}
const rootElement = document.getElementById("root");
ReactDOM.render(<ButtonClicks />, rootElement);
export default ButtonClicks;
i suggest that you give your buttons the IDs of their index and receiving them in the onClickFunction like this
onClickFunction(e) {
var mp3Index = parseInt(e.target.id);
console.log("button ");
console.log(this.props.log);
}
render() {
for (let i = 0; i < 10; i++) {
//Moved your loop outside render()'s return
arrButtons.push(
<button id={i} style={buttonStyle} onClick={this.onClickFunction}>
{i}
{this.props.i}
</button>
);
}
return (
<div>
{arrButtons} {/*Very important to wrap the buttons inside a div*/}
</div>
);
}
}
from there you may proceed on how to use their index.