Why does my "Audio-Button" don`t play a sound (onClick) - reactjs

I am struggeling on finding out why my button dont play a sound when I click on it. The console.log() test works fine, but the -part dont. I also tried some npm-packets to solve the problem, but it seems like my code has a general problem. Whats wrong with it? Can someone help me?
The main.js :
import Button from './button';
class Drumpad extends Component {
constructor(props) {
super(props);
this.state = {
Q:
{
id: 'Q',
name: 'Q',
src: 'https://s3.amazonaws.com/freecodecamp/drums/Heater-1.mp3'
},
}
}
render() {
return (
<div style={test}>
<div id='row1'>
<Button cfg={this.state.Q}/>
</div>
</div>
)
}
}
And the button.js:
class Button extends Component {
constructor(props) {
super(props);
this.state = {
}
}
handleClick = () => {
console.log(this.props.cfg.src);
return (
<audio ref='audioClick' src={this.props.cfg.src} type='audio/mp3' autoPlay>
);
};
render() {
return (
<div>
<button style={buttonStyle} onClick={this.handleClick}>
<h1>{this.props.cfg.name}</h1>
</button>
</div>
)
}
}

The handleClick method in button.js returns an <audio> element, which is redundant, since you would like to play the sound onClick.
Instead I used a Audio constructor to create an instance of the audio clip, using the url provided as props, which I set to state.
Then I use a callback to invoke the play() method on it.
handleClick = () => {
const audio = new Audio(this.props.cfg.src);
this.setState({ audio }, () => {
this.state.audio.play();
});
};
So your button.js becomes something like this:
import React, { Component } from "react";
const buttonStyle = {};
export default class Button extends Component {
constructor(props) {
super(props);
this.state = {
audio: false
};
}
handleClick = () => {
console.log(this.props.cfg.src);
const audio = new Audio(this.props.cfg.src);
this.setState({ audio }, () => {
this.state.audio.play();
});
};
render() {
return (
<div>
<button style={buttonStyle} onClick={this.handleClick}>
<h1>{this.props.cfg.name}</h1>
</button>
</div>
);
}
}
Your main.js remains as is.
Here is a working codesandbox.

Related

REACTJS JSX re render

I want to re-render html in App.js what is triggered by click event.
In first load JSX component <WaypointList waypoint_is_done={false} App={this}/> is rendered.
But when i click button then it wont render JSX component <WaypointList waypoint_is_done={true} App={this}/> again.
class App extends React.Component {
constructor(props) {
super(props);
this.state = {
content: this.index()//LETS LOAD CONTENT
};
this.index = this.index.bind(this);
this.toggleDoneList = this.toggleDoneList.bind(this);
};
index() {
return <WaypointList waypoint_is_done={false} App={this}/>;
};
//SET NEW RENDERER ONCLICK
toggleDoneList(){
console.log('click');
this.setState({
content: <WaypointList waypoint_is_done={true} App={this}/>
//content: <div>see</div>
});
};
render() {
console.log(this.state.content);
return this.state.content;
};
}
ReactDOM.render(
<App/>,
document.getElementById('app')
);
First time it fire WaypointList class, but when i click button "object-done-listing" then not
It calls the App.toggleDoneList and App.render is also fired and it get correct JSX component but does not fire WaypointList class again
class WaypointList extends React.Component {
constructor(props) {
super(props);
this.App = props.App;
this.state = {
content: this.index(props)
};
this.index = this.index.bind(this);
};
index(props) {
let rows = logistic_route_sheet_waypoint_rows;
if (rows.length > 0) {
return (
<div className="map-listing">
<div className="object-done-listing noselect btn btn-success"
onClick={() => this.App.toggleDoneList()}>
<i className="fa fa-list" aria-hidden="true"></i>
</div>
</div>
);
}
return (null);
};
render() {
return this.state.content;
};
}
It works if i set
this.setState({
content: <div>see</div>
});
but not with
this.setState({
content: <WaypointList waypoint_is_done={true} App={this}/>
});
What is the problem ?
I found a solution to re-renderer the class
i made "CustomEvent" "reRenderer" and i call re_renderer function outside of react.

Unhandled Rejection (TypeError): Cannot read property 'setState' of undefined

I know that there are plenty of answers on this, for example this one. I did add the .bind(this) in the component constructor. I also tried the fat arrow method (fakeApiCall = ()=>{ ... }) but when I click Change Me, this error still displays:
import React, { Component } from 'react';
import axios from 'axios';
class App extends Component {
constructor(props){
super(props);
this.state = {
count : 1000
};
this.fakeApiCall = this.fakeApiCall.bind(this);
}
fakeApiCall (){
axios.get('https://jsonplaceholder.typicode.com/users')
.then(function(response){
// the response comes back here successfully
const newCount = response.data.length;
// fail at this step
this.setState({ count : Math.floor(newCount) });
});
}
render() {
return (
<div className="App">
<span style={{ fontSize : 66 }}>{this.state.count}</span>
<input type='button' onClick={this.fakeApiCall} value='Change me' />
</div>
);
}
}
export default App;
Your fakeApiCall function is bound to your context, but the function callback in axios is not.
To solve this, you can use an arrow function, as they automatically bind with your class. You can also do it for fakeApiCall and remove it's binding :
class App extends Component {
constructor(props) {
super(props);
this.state = {
count: 1000
};
}
fakeApiCall = () => {
axios.get('https://jsonplaceholder.typicode.com/users')
.then(response => { //This is an arrow function
const newCount = response.data.length;
this.setState({ count: Math.floor(newCount) });
});
}
render() {
return (
<div className="App">
<span style={{ fontSize: 66 }}>{this.state.count}</span>
<input type='button' onClick={this.fakeApiCall} value='Change me' />
</div>
);
}
}

Click Handle on Jest

I am writing a test case using jest, but I am not able to get how to test click simulation if it is not button.
If it is button we write find('button), but what if we click on div and there are nested div
class Section extends React.Component {
constructor(props) {
super(props);
this.state = {
open: props.open,
className: 'accordion-content accordion-close',
headingClassName: 'accordion-heading'
};
this.handleClick = this.handleClick.bind(this);
}
handleClick() {
this.setState({
open: !this.state.open
});
}
render() {
const { title, children } = this.props;
const { open } = this.state;
const sectionStateClassname = open
? styles.accordionSectionContentOpened
: styles.accordionSectionContentClosed;
return (
<div className={styles.accordionSection}>
<div
className={styles.accordionSectionHeading}
onClick={this.handleClick}
id="123"
>
{title}
</div>
<div
className={`${
styles.accordionSectionContent
} ${sectionStateClassname}`}
>
{children}
</div>
</div>
);
}
}
here is my jest test case
test('Section', () => {
const handleClick = jest.fn();
const wrapper = mount(<Section onClick={ handleClick} title="show more"/>)
wrapper.text('show more').simulate('click')
expect(handleClick).toBeCalled()
});
You can find element by class:
wrapper.find('.' + styles.accordionSectionHeading).first().simulate('click')
Also, your component seems to not call prop handleClick. Instead, instance method is called, so something like this:
wrapper.instance().handleClick = jest.fn();
expect(wrapper.instance().handleClick).toBeCalled();
seems to be more correct.
Or, better, you can just check if state is changed
expect(wrapper.state('open')).toBeTruthy();
Hope it helps.

I can not access the right data of property sent from parent to child component

I am facing an issue with react and I am totally stuck. I have 3 components: channel as a parent and header and story as a children:
class Channel extends React.Component {
constructor(props) {
super();
}
componentDidMount() {
this.props.getChannels();
}
render() {
return (
<div>
<div className="col-xs-12 col-md-8 col-lg-8>
<div className="row">
<Header activeChannelList={this.props.channels.channelsArr}/>
</div>
<div className="row">
{
this.props.channels.channelsArr.map((item, i) => <StoryBoard
newsChanel={item}
key={"storyBoard" + i}
></StoryBoard>)
}
</div>
</div>
<div className="col-xs-12 col-md-2 col-lg-2 color2">.col-sm-4</div>
</div>
);
}
}
const mapStateToProps = (state) => {
return {
channels: state.channelReducer
};
};
const mapDispatchToProps = (dispatch) => {
return {
getChannels: () => {
dispatch(getChannels());
}
};
};
export default connect(mapStateToProps, mapDispatchToProps)(Channel);
As you can see I have a ajax call with this.props.getChannels(); and I put it in componentDidMount to make sure that it is called before rendering then after I pass the channels to the Header ans story which are children components.
Now my problem is when I try to access it in Header via console.log(this.props.activeChannelList); I get 0 thought I should have 5 channels. More intrestingly when I try to access the props I send in Stroryboard I can easily access them without any problem. The following is my code for Header:
export class Header extends React.Component {
constructor(props) {
super();
}
componentDidMount() {
console.log("dddddddddddddddddddddddddddddddddddddddddd");
console.log(this.props.activeChannelList);// I get 0 though I should get 5
}
render() {
return (
<div className="col-xs-12 header tjHeaderDummy">
<div className="col-xs-1"></div>
</div>
);
}
}
And my storyboard is :
class StoryBoard extends React.Component {
constructor(props) {
super();
}
componentDidMount() {
if(this.props.isFreshLoad ){
do sth
}
}
render() {
return (
<div>
</div>
);
}
}
const mapStateToProps = (state) => {
return {
stories: state.storyBoardReducer
};
};
const mapDispatchToProps = (dispatch) => {
return {
//some funcs
}
};
};
export default connect(mapStateToProps, mapDispatchToProps)(StoryBoard);
Can anyone help?
U r printing the value in componentDidMount method in Header component, this lifecycle method get called only once, if ur api response come after the rendering of Header, it will never print 5, put the console in render method, so that at any time when u get the response it will populate the value.
From Docs:
componentDidMount: is invoked immediately after a component is mounted
first time. This is where AJAX requests and DOM or state updates
should occur.
Try this Header Comp, it will print the proper value:
export class Header extends React.Component {
constructor(props) {
super();
}
componentDidMount() {
}
render() {
return (
<div className="col-xs-12">
{this.props.activeChannelList}
</div>
);
}
}

JSX props should not use .bind()

How to fix this error when I have the binding this way: previously binding in constructor solved but this is a bit complex for me:
{this.onClick.bind(this, 'someString')}>
and
<form onSubmit={handleSubmit(this.handleFormSubmit.bind(this))}>
Option 1:
Use arrow functions (with babel-plugins)
PS:- Experimental feature
class MyComponent extends Component {
handleClick = (args) => () => {
// access args here;
// handle the click event
}
render() {
return (
<div onClick={this.handleClick(args)}>
.....
</div>
)
}
}
Option 2: Not recommended
Define arrow functions in render
class MyComponent extends Component {
render() {
const handleClick = () => {
// handle the click event
}
return (
<div onClick={handleClick}>
.....
</div>
)
}
}
Option 3:
Use binding in constructor
class MyComponent extends Component {
constructor() {
super();
this.handleClick = this.handleClick.bind(this);
}
handleClick() {
// handle click
}
render() {
return (
<div onClick={this.handleClick}>
.....
</div>
)
}
}
I recommend you to use binding in the class constructor. This will avoid inline repetition (and confusion) and will execute the "bind" only once (when component is initiated).
Here's an example how you can achieve cleaner JSX in your use-case:
class YourComponent extends React.Component {
constructor(props) {
super(props);
// Bind functions
this.handleClick = this.handleClick.bind(this);
this.handleSubmit = this.handleSubmit.bind(this);
}
handleClick() {
this.onClick('someString');
}
onClick(param) {
// That's your 'onClick' function
// param = 'someString'
}
handleSubmit() {
// Same here.
this.handleFormSubmit();
}
handleFormSubmit() {
// That's your 'handleFormSubmit' function
}
render() {
return (
<form onSubmit={this.handleSubmit}>
<p>...</p>
<button onClick={this.handleClick} type="button">Cancel</button>
<button type="submit">Go!</button>
</form>
);
}
}
Even though all the previous answers can achieve the desire result, but I think the snippet below worth mentioning.
class myComponent extends PureComponent {
handleOnclickWithArgs = arg => {...};
handleOnclickWithoutArgs = () => {...};
render() {
const submitArg = () => this.handleOnclickWithArgs(arg);
const btnProps = { onClick: submitArg }; // or onClick={submitArg} will do
return (
<Fragment>
<button {...btnProps}>button with argument</button>
<button onClick={this.handleOnclickWithoutArgs}>
button without argument
</button>
</Fragment>
);
}
}

Resources