How to link onclick callback to call external file - javascript

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.

Related

Generating inputs in a React component

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

How to update one child component alone in React Js

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.

How to load a new component on button click in ReactJs?

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" />

How to pass value to onclick callback using this.props

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');
}

Show the last index in an array when I click (in an array that continues to be updated)

I am near the end of creating my application.
So it is for banks accounts where they ask you to give the first letter of your password, then for example fourth, etc.
I'm tired of counting on my own so I created this app.
But there is the last bug that I don't know how to fix.
So when I press "1" I get "1 - H", and then when I press "4" I want to get:
"1 - H" (clicked before)
"4 - X" (clicked just now)
but instead, I get:
"4 - X" (clicked just now)
"4 - X" (clicked just now)
So it is caused by the way handleResults() function works inside my Input component, but for now it is my only concept how to approach this...
import React, { Component } from 'react';
import TextField from 'material-ui/TextField';
import './style.css';
import Buttons from '../Buttons';
import Results from '../Results';
class Input extends Component {
constructor(props) {
super(props);
this.state = {
password: 'Hh9Xzke2ayzcEUPHuIfS',
selectedButtons: [],
};
this.handleButtonSelectTwo = this.handleButtonSelectTwo.bind(this);
}
handleInputChange(pass) {
this.setState({ password: pass });
}
handleButtonSelectTwo(selected) {
this.setState({
selectedButtons: [...this.state.selectedButtons, selected],
});
}
handleResults() {
return this.state.selectedButtons.map(el => (
<Results key={el} appState={this.state} />
));
}
render() {
return (
<div>
<div className="Input-textfield">
<TextField
hintText="Paste your password here to begin"
value={this.state.password}
onChange={event => this.handleInputChange(event.target.value)}
/>
</div>
<div>
<Buttons
handleButtonSelectOne={this.handleButtonSelectTwo}
array={this.state.password.length}
/>
{this.handleResults()}
</div>
</div>
);
}
}
export default Input;
and here is Results component code:
import React, { Component } from 'react';
import _ from 'lodash';
import Avatar from 'material-ui/Avatar';
import List from 'material-ui/List/List';
import ListItem from 'material-ui/List/ListItem';
import './style.css';
const style = {
avatarList: {
position: 'relative',
left: -40,
},
avatarSecond: {
position: 'relative',
top: -40,
left: 40,
},
};
class Results extends Component {
resultsEngine(arg) {
const { selectedButtons, password } = this.props.appState;
const passwordArray = password.split('').map(el => el);
const lastSelectedButton = _.last(selectedButtons);
const passwordString = passwordArray[_.last(selectedButtons) - 1];
if (arg === 0) {
return lastSelectedButton;
}
if (arg === 1) {
return passwordString;
}
return null;
}
render() {
if (this.props.appState.selectedButtons.length > 0) {
return (
<div className="test">
<List style={style.avatarList}>
<ListItem
disabled
leftAvatar={<Avatar>{this.resultsEngine(0)}</Avatar>}
/>
<ListItem
style={style.avatarSecond}
disabled
leftAvatar={<Avatar>{this.resultsEngine(1)}</Avatar>}
/>
</List>
</div>
);
}
return <div />;
}
}
export default Results;
Anyone has an idea how should I change my code inside handleResults() function to achieve my goal? Any help with solving that problem will be much appreciated.
Buttons component code:
import React from 'react';
import OneButton from '../OneButton';
const Buttons = props => {
const arrayFromInput = props.array;
const buttonsArray = [];
for (let i = 1; i <= arrayFromInput; i++) {
buttonsArray.push(i);
}
const handleButtonSelectZero = props.handleButtonSelectOne;
const allButtons = buttonsArray.map(el => (
<OneButton key={el} el={el} onClick={handleButtonSelectZero} />
));
if (arrayFromInput > 0) {
return <div>{allButtons}</div>;
}
return <div />;
};
export default Buttons;
And OneButton code:
import React, { Component } from 'react';
import RaisedButton from 'material-ui/RaisedButton';
const style = {
button: {
margin: 2,
padding: 0,
minWidth: 1,
},
};
class OneButton extends Component {
constructor() {
super();
this.state = { disabled: false };
}
handleClick() {
this.setState({ disabled: !this.state.disabled });
this.props.onClick(this.props.el);
}
render() {
return (
<RaisedButton
disabled={this.state.disabled}
key={this.props.el}
label={this.props.el}
style={style.button}
onClick={() => this.handleClick()}
/>
);
}
}
export default OneButton;
In your resultsEngine function in the Results component you are specifying that you always want the _.last(selectedButtons) to be used. This is what it is doing, hence you always see the last button clicked. What you actually want is the index of that iteration to show.
const lastSelectedButton = selectedButtons[this.props.index];
const passwordString = passwordArray[selectedButtons[this.props.index]];
To get an index you have to create and pass one in, so create it when you map over the selected Buttons in the handleResults function in your Input component.
handleResults() {
return this.state.selectedButtons.map((el, index) => (
<Results key={el} appState={this.state} index={index} />
));
}

Categories