Reactjs calling another component when clicking a button - reactjs

Currently, I am working on a simple system where after a button is pushed another webpage opens up. However, I am aiming to display a component after the button is clicked. Hereby, I initialized a state with a boolean set to false. Furthermore, after the button is clicked the state should be set to true which should enable the display of the component ApartmentBooking01. When running the code a new webpage shows up due to the history.push object. However, it does not display my wanted component ApartmentBooking01. Can anyone maybe explain to me what I am doing wrong?
class ApartmentInformation01 extends React.Component {
constructor(props){
super(props);
this.state = {
showBookingInformation: false,
apartmentInformation: [{
apartmentNumber: [],
availableBeds: [],
pricePerNight: []
}]
};
this.handleClick = this.handleClick.bind(this);
}
handleClick(event) {
this.props.history.push("api/apartments/apartmentbooking01");
this.setState({
showBookingInformation: !this.state.showBookingInformation
});
}
getApartmentBookingComponent(){
if(this.state.showBookingInformation){
return <ApartmentBooking01/>
}else {
return null;
}
}
componentDidMount() {
axios.get(`http://localhost:8080/api/apartment/`)
.then(res => {
const apartmentInformation = res.data;
this.setState({ apartmentInformation });
})
}
render() {
const { apartmentInformation } = this.state;
return (
<div className='apartmentInformation-container'>
<ul>
{
apartmentInformation.filter(apartmentInfo => apartmentInfo.apartmentNumber == 1)
.map(filteredApartment => (
<div className='apartmentInformation-items'>
<h2 className='apartmentssection-01-price'>Price per Night</h2>
<p className='apartmentInformation-items-pricePerNight'>€{filteredApartment.pricePerNight},- </p>
<h2 className='apartmentssection-01-beds'>Available Beds</h2>
<p className='apartmentInformation-items-availableBeds'>{filteredApartment.availableBeds} Beds</p>
</div>
))
}
</ul>
<button variant="btn-success" onClick={this.handleClick}>More information</button>
{this.getApartmentBookingComponent}
</div>
)
}
}
export default withRouter (ApartmentInformation01)

Try to add event.preventDefault() at handleClick function. This should be in the first line.
also you should add keys for you map method:
<div className='apartmentInformation-items' key={appId}>

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.

how to handle multiple instance of audio files in react-wavesurfer for reactJS?

I am using wavesurfer package to play audio files. I am using state variable to play, pause audio files which are in loop but i am facing issue to play specific file when i press play button it plays all file because i am using state variable to play as 'playing' variable.please see above code.
import React, { Component } from 'react';
import Wavesurfer from 'react-wavesurfer';
window.WaveSurfer = require("wavesurfer.js");
let Regions = require("react-wavesurfer/lib/plugins/regions").default;
let Minimap = require("react-wavesurfer/lib/plugins/minimap").default;
class DashboardPage extends Component{
constructor(props){
super(props);
this.state = {
recordings:objectOfRecordings,
playing: false,
pos: 0
};
this.handleChange = this.handleChange.bind(this);
this.handleTogglePlay = this.handleTogglePlay.bind(this);
this.handlePosChange = this.handlePosChange.bind(this);
};
handleTogglePlay() {
this.setState({
playing: !this.state.playing
});
}
handlePosChange(e) {
this.setState({
pos: e.originalArgs[0]
});
}
render(){
const { recordings } = this.state;
return(
<div>
<h1>Dashboard</h1>
<div className="recording">
<ul className="list-group">
{
recordings &&
recordings.map((prop,key)=>{
return (
<li className="list-group-item" key={key}>
<Wavesurfer
audioFile={prop.comment_url}
pos={this.state.pos}
onPosChange={this.handlePosChange}
playing={this.state.playing}
/>
<button onClick={this.handleTogglePlay}>play</button>
</li>
)
})
}
</ul>
</div>
</div>
);
}
}
export default Dashboard;
Please suggest me better solution to play specific file.
This is happening because the central state for configuring multiple recordings in stored in the parent component with a boolean variable.
There are two options forward from here:
Move the playing boolean playing state and position state to inside a separate Recording component, because they are internal to it. But this comes with a disadvantage. You cannot stop the one already playing before starting the next, (this is not stated in the question, I'm just guessing)
The second would be to store the ID of the recording being played inside playing state variable and using it to trigger wavesurfer. This approach allows you to have exactly one playing at a time. To control the position individually, it is moved inside a separate component.
For eg:
import React, { Component } from 'react';
import Wavesurfer from 'react-wavesurfer';
window.WaveSurfer = require("wavesurfer.js");
let Regions = require("react-wavesurfer/lib/plugins/regions").default;
let Minimap = require("react-wavesurfer/lib/plugins/minimap").default;
class Recording extends Component {
constructor(props) {
this.state = {
pos: 0,
}
this.handleTogglePlay = this.handleTogglePlay.bind(this);
this.handlePosChange = this.handlePosChange.bind(this);
}
handleTogglePlay() {
const { id, onChange } = this.props;
onChange(id);
}
handlePosChange(e) {
this.setState({
pos: e.originalArgs[0]
});
}
render() {
const { data } = this.props;
return (
<li className="list-group-item">
<Wavesurfer
audioFile={data.comment_url}
pos={this.state.pos}
onPosChange={this.handlePosChange}
playing={this.props.playing}
/>
<button onClick={this.handleTogglePlay}>play</button>
</li>
);
}
}
class DashboardPage extends Component{
constructor(props){
super(props);
this.state = {
playing: -1,
recordings:objectOfRecordings,
};
this.handleChange = this.handleChange.bind(this);
this.changePlaying = this.changePlaying.bind(this);
};
changePlaying(id) {
this.setState({
playing: id,
});
}
render(){
const { recordings } = this.state;
return(
<div>
<h1>Dashboard</h1>
<div className="recording">
<ul className="list-group">
{
recordings &&
recordings.map((prop,key)=>{
return (
<Recording
data={prop}
key={key}
id={key}
playing={key === this.state.playing}
onChange={this.changePlaying}
/>
)
})
}
</ul>
</div>
</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.

React: Issues with Conditional Rendering

In my React-App, i use the Firebase SDK. If a user wants to reset his password, he will be redirected to a page within my app. If the code is valid, the component <PWResetConfirmForm /> should be rended. If the code is invalid, the component <PWResetOutdatedForm /> is to be rendered.
My Page Component looks like this:
class PWResetConfirmPage extends Component {
constructor(props) {
super(props);
this.state = {};
this.verfiyResetPassword = this.verfiyResetPassword.bind(this);
}
verfiyResetPassword() {
const params = (new URL(`http://dummy.com${this.props.location.search}`)).searchParams;
const code = params.get("oobCode")
auth.doVerfiyPasswordReset(code)
.then(function () {
return (
<div className="HomePage-Main">
<TopBar></TopBar>
<PWResetConfirmForm></PWResetConfirmForm>
</div>
);
})
.catch(function () {
return (
<div className="HomePage-Main">
<TopBar></TopBar>
<PWResetOutdatedForm></PWResetOutdatedForm>
</div>
);
})
}
render() {
return (
<div>
{this.verfiyResetPassword()}
</div>
);
}
}
export default PWResetConfirmPage
When i try to run, i get a blank page and not error.
Where is my issue and how can i fix that?
Thank you very much for your help and for your time
You will not be able to return JSX from within then()/catch() of auth.doVerfiyPasswordReset() like that. You can instead approach this by taking advantage of React.Component lifecycle method componentDidMount and using setState() to manipulate state properties for conditional rendering. I've added state properties to the component, one to track whether loading (API call has completed) and one to track whether the call was a success (then) or failure (catch). These properties are used to conditionally generate JSX content for rendering. This is assuming that verfiyResetPassword() is intended to run when the component is first mounted, instead of every time render() is called:
class App extends Component {
constructor() {
super();
this.state = {
isResetVerified: null,
loading: true
};
}
componentDidMount() {
this.verfiyResetPassword();
}
verfiyResetPassword() {
const params = (new URL(`http://dummy.com${this.props.location.search}`)).searchParams;
const code = params.get("oobCode")
auth.doVerfiyPasswordReset('foobar')
.then(() => {
this.setState({
...this.state,
isResetVerified: true,
loading: false
});
})
.catch(() => {
this.setState({
...this.state,
isResetVerified: false,
loading: false
});
})
}
getContent() {
if (this.state.loading) {
return (
<div>Loading...</div>
);
} else {
if (this.state.isResetVerified) {
return (
<div className="HomePage-Main">
<TopBar></TopBar>
<PWResetConfirmForm></PWResetConfirmForm>
</div>
);
} else {
return (
<div className="HomePage-Main">
<TopBar></TopBar>
<PWResetOutdatedForm></PWResetOutdatedForm>
</div>
);
}
}
}
Here is a basic example in action.
Also, in the constructor this.verfiyResetPassword = this.verfiyResetPassword.bind(this); would only be needed if verfiyResetPassword() is executed by a DOM event such as button onClick or similar.
Hopefully that helps!
I could still fix the error myself:
class PWResetConfirmPage extends Component {
constructor(props) {
super(props);
this.state = {
isValid: false,
code: "",
};
this.verfiyResetPassword = this.verfiyResetPassword.bind(this);
}
componentDidMount() {
const params = (new URL(`http://dummy.com${this.props.location.search}`)).searchParams;
const code = params.get("oobCode")
this.setState({code:code})
auth.doVerfiyPasswordReset(code)
.then(() => {
this.setState({
...this.state,
isValid: true,
});
})
.catch(() => {
this.setState({
...this.state,
isValid: false,
});
})
}
verfiyResetPassword() {
if (this.state.isValid) {
return (
<div>
<TopBar></TopBar>
<PWResetConfirmForm code={this.state.code}></PWResetConfirmForm>
</div>
);
} else {
return (
<div>
<TopBar></TopBar>
<PWResetOutdatedForm></PWResetOutdatedForm>
</div>
);
}
}
render() {
return (
<div className="HomePage-Main">
{this.verfiyResetPassword()}
</div>
);
}
}
export default PWResetConfirmPage

All the toasters close when clicked on the close button in react

I have made a toaster component of my own which on multiple clicks render multiple toasters. The problem I am facing is that all the toasters are terminated when the handle close component is clicked or when the settimeout function is called. I am passing messages through another component as props.
This is my toaster component
export default class MyToaster extends React.Component {
constructor(props) {
super(props);
this.state = {
message: props.message,
show: false,
no: 0
};
}
handleclose = () => {
this.setState({
show: false,
no: this.state.no - 1
})
}
handleOpen = () => {
console.log('HANDLE OPEN')
this.setState({
show: true,
no: this.state.no + 1
}, () => {
setTimeout(() => {
this.setState({
show: false,
no: this.state.no - 1
})
}, 3000)
})
}
createtoaster = () => {
if (this.state.show) {
let toastmessage = [];
for (let i = 0; i < this.state.no; i++) {
let tmessage = <div className="snackbar">
<div className="card-header">
<h3 className="card-title">Toast</h3>
</div>
<div className="card-body">
{this.state.message}
</div>
<div className="card-footer"></div>
<button className="btn" onClick={this.handleclose}>x</button>
</div>
toastmessage.push(tmessage);
}
return toastmessage;
} else {
return null;
}
};
render() {
return (
<div className="col-md-2 offset-md-9">
<button className="btn btn-primary" onClick={this.handleOpen}></button>
{this.createtoaster()}
</div>
)
}
}
I have tried managing the state in the parent component but it doesnt seem to work. I do know that the problem is in managing state of my toaster component but dont know the exact problem and the solution.
Any solutions for this also feel free to point out any of my mistakes.
TIA
Handle close is run on the click of any button rather on the instance of one of them by the looks of it.
if (this.state.show) { // this determines whether to render you toasts...
// and close turns all of them off.
You need to change each toast to have it's own show property and for close to toggle that one and remove it from the array of toasts to generate.
Note:
Your props and state should be separate, don't copy props into state as this will introduce bugs and changes will not be reflected.
constructor(props) {
super(props);
// avoid copying props into state
// https://reactjs.org/docs/react-component.html#constructor
this.state = {
message: props.message,
show: false,
no: 0
};
}
There is a different way to this approach.
export default class MyToaster extends React.Component {
constructor(props) {
super(props);
this.state = {
message: props.message,
show: true,
no: 0
};
}
componentDidMount() {
setTimeout(() => {
this.setState({show: false})
}, 4000)
}
handleclose = () => {
this.setState({
show: false,
no: this.state.no - 1
})
}
handleOpen = () => {
this.setState({
no: this.state.no + 1
}, () => {
setTimeout(() => {
this.setState({
show: false,
no: this.state.no - 1
})
}, 3000)
})
}
render() {
return (
<div className="col-md-2 offset-md-9">
{this.state.show
? (
<div className="container snackbar" style={this.props.style}>
<div className="card-header">
<h3 className="card-title">Toast</h3>
</div>
<div className="card-body">
{this.props.message}
</div>
<div className="card-footer"></div>
</div>
)
: null
}
</div>
)
}
}
And from your parent component you can include
this.state = {
toasterCollection: []
}
//make a function
handleToasterClick = () => {
const toaster = <Toaster message={this.message} style={this.style}/>
this.setState({
// toasterCollection: [...this.state.toasterCollection, toaster]
toasterCollection: [...this.state.toasterCollection, toaster]
});
}
//In your Render give a button
<button className="btn btn-primary" onClick={this.handleToasterClick}>
Toast
</button>
//Also render this
{this.state.toasterCollection}
This should get your code to work.

Resources