class App extends React.Component {
constructor() {
super();
this.state = {
activeTab: "add",
items: [],
}
}
handleClick(activeTab) {
switch (activeTab) {
case 'add': return <Add/>
case 'list': return <List />
case 'pay': return <Pay />
}
}
render() {
return (
<div className="App btn-group">
<Button onClick={this.handleClick.bind(this, 'add')}><Add /></Button>
<Button onClick={this.handleClick.bind(this, 'list')}><List /></Button>
<Button onClick={this.handleClick.bind(this, 'pay')}><Pay /></Button>
</div>
)
}
}
export default App;
I wish through this code in reactjs, When I onClick on an 'add' button it shows me the Add components but unfortunately it does not work. On the other hand in my switch when I do an alert () or a console.log () instead of returning my components; it works well as it should.
the <Add /> List and Pay are components and I have imported into the app.js.
there are several issues with your code.
if you want to show the components based on your active Tab you have to maintain the state variable . I see you have set your initial state activeTab : "add" but trying to render <Add /> component inside a button . that's why it doesn't work as expected . you have to render it separately .
your handleClick function is supposed to do something . not return something. so here if you return your components inside switch that won't work. you can change the state here.
you can use the conditional rendering feature of react to render the desired component based on your active tab as shown below.
here is the complete component code that should solve the problems i mentioned above
class App extends React.Component {
constructor() {
super();
this.state = {
activeTab: "add",
items: [],
}
}
handleClick(activeTab) {
this.setState({activeTab})
}
render() {
return (
<div>
<div className="App btn-group">
<Button onClick={this.handleClick.bind(this, 'add')}> Add </Button>
<Button onClick={this.handleClick.bind(this, 'list')}> List </Button>
<Button onClick={this.handleClick.bind(this, 'pay')}> Pay </Button>
</div>
{this.state.activeTab === 'add' && <Add /> }
{this.state.activeTab === 'list' && <List /> }
{this.state.activeTab === 'pay' && <Pay /> }
</div>
)
}
}
export default App;
Related
I'm trying to submit a Form from Child Component through a button placed in Parent. The Parent is a Slide which has a Next Button and I display my Child Component in one page of a Slide.
What I want to do is: When I press next button from Parent I have to go to the next slide and also to submit the Form Child.
code:
Parent Component:
import Slider from "react-slick";
class Parent extends React.Component {
constructor(props) {
super(props)
this.state = {
errors: {}
}
next() {
this.slider.slickNext();
}
previous() {
this.slider.slickPrev();
}
render() {
const settings = {
dots: true,
infinite: false
};
return (
<div>
<Slider ref={c => (this.slider = c)} {...settings}>
<div style={{ textAlign: "center" }} key={1} >
<Child /> //this is the Child I want to submit once I press Next Button
<Button id="btn" className="btn btn-primary float-right " type="submit" onClick={this.next}> Next</Button> //this is the next button which sould trigger the submit from Child
</div>
<div key={2} >
<Page2/>
<Button className="btn btn-primary float-right " id="btn" onClick={() => this.finish()>Finish</Button>
<Button id="btn" className="btn btn-primary float-right " onClick={this.previous}> Previous</Button>
</div>
</Slider>
</div>
);
}
}
Child Component:
class Child extends React.Component {
constructor(props) {
super(props)
this.state = {
info: '',
infoID: 0,
valid:false,
errors: {}
}
this.handleValidSubmit = this.handleValidSubmit.bind(this);
this.onChange = this.onChange.bind(this);
}
handleValidSubmit() {
if(valid){
localStorage.setItem("infoID",this.state.infoID);
this.sendIDToServerAPI(infoID);
}else {
localStorage.setItem("infoID",this.state.infoID);
this.saveNewInfoAPI(info);
}
}
render() {
return (
<div }>
<Container fluid >
<Row >
<Col >
<AvForm id="avForm" onValidSubmit={this.handleValidSubmit} onInvalidSubmit=}>
<Label >Info</Label>
<AvField onChange={this.onChange} name="email" placeholder=""Email/>
........
.......
</AvForm>
</Col>
</Row>
</Container>
</div>
);
}
}
I tried to minimise the components because they are too large, I hope is clear enough what I try to do. Can anyone please help me with a solution/idea? I tried to Google it but couldn't find anything helpful.
Thanks a lot
What you can do is keep a state(a flag) something like submit which will initially be false.
class Parent extends React.Component {
constructor(props) {
super(props)
this.state = {
submit: false
....rest of your code
}
// function to update the submit state
const setFormSubmit = () => this.setState({submit: true})
}
Now you call setFormSubmit on next button click
Now you can pass this state(submit) to your child component.
and whenever the submit is true you will trigger the method to submit the form
You can do this by using componentDidUpdate in your child component.
componentDidUpdate(prevProps, prevState) {
// if prev submit and current submit prop is unequal and the submit is true
if ((prevProps.submit !== this.props.submit) && this.props.submit) {
// Call method to submit form
}
}
I am fairly new to Reactjs and am running into an issue. I am using Griddle to display my data and using a Custom Component as a Column Definition. This custom component is defined outside of the class that contains the Griddle render and contains two buttons - Delete and Edit. I need to be able to click on either button and launch a modal, which I am using React Modal for. If I put a button within the class I can launch the modal as expected, however, when I am trying to launch the modal from one of the Edit or Delete buttons it does not work and I get an error
Uncaught TypeError: M.handleOpenModal is not a function.
Here is the code for the component that uses the Griddle plugin:
const actionComponent = ({ value }) =>
<div>
<i className="fas fa-edit"></i> Edit
<i className="fas fa-times"></i> Delete
</div>
const handleModalOpen = function () {
LinersTable.handleOpenModal();
}
export default class LinersTable extends React.Component {
constructor(props) {
super(props);
this.state = { liners: [], station: '', loading: true, selectedRowId: 0 };
this.modalElement = React.createRef();
this.options = {
defaultSortName: 'title', // default sort column name
defaultSortOrder: 'desc' // default sort order
};
}
handleOpenModal() {
alert();
}
render() {
let contents = this.state.loading ? <p><em>Loading...</em></p> : this.renderLinersTable(this.state.liners);
return <div>
<button className='btn btn-primary' onClick={() => { this.refreshData() }}>Refresh</button>
<a href="LinerReads/Studio" className='btn btn-primary'>Studio View</a>
<a href="LinerReads/Add" className='btn btn-primary'>Add</a>
<LinersStations onChange={this.handleStationChange.bind(this)} />
<ModalWindow ref={this.modalElement} />
{contents}
</div>;
}
renderLinersTable(liners) {
return (
<Griddle
styleConfig={styleConfig}
data={liners}
plugins={[plugins.LocalPlugin]}
components={{ Layout: NewLayout }}>
<RowDefinition>
<ColumnDefinition id="title" title="Title" order={1} width={600} />
<ColumnDefinition id="startDateString" title="Start" order={2} width={200} />
<ColumnDefinition id="endDateString" title="End" order={3} width={200} />
<ColumnDefinition id="linerID" title="Actions" customComponent={actionComponent} width={200} />
</RowDefinition>
</Griddle>
)
}
}
I have tried moving the Custom Component inside the class
handleModalOpen = function () {
this.handleOpenModal();
}
handleOpenModal() {
alert();
}
but it then throws an error
ReferenceError: handleModalOpen is not defined
Uncaught (in promise) ReferenceError: handleModalOpen is not defined
Anyone have a suggestion as to what I am missing or doing incorrect?
Hi I am playing around with ReactJS, and found this awesome Modal Component to open Videoes in a Modal, but when I put the Modal inside a loop with multiple links and open the modal, it open like 5 times if I have 5 links. What do I do wrong?
Modal Component: https://github.com/appleple/react-modal-video
import React from 'react'
import ReactDOM from 'react-dom'enter code here
import ModalVideo from 'react-modal-video'
class App extends React.Component {
constructor () {
super()
this.state = {
isOpen: false
}
this.openModal = this.openModal.bind(this)
}
openModal () {
this.setState({isOpen: true})
}
render () {
return (
<div>
<ModalVideo channel='youtube' isOpen={this.state.isOpen} videoId='L61p2uyiMSo' />
<button onClick={this.openModal}>Open</button>
</div>
)
}
}
ReactDOM.render(
<App />,
document.getElementById('root')
)
My Loop with the Modal Component Inside:
render(){
return(
<div>
{(this.props.frag.all == null) ? null :
this.props.frag.all.map((frags, i) => {
return (
<li key={frags.id} className="group" id="" style={{width: 'calc(13% - 30px)'}}>
<ModalVideo channel='youtube' isOpen={this.state.isOpen} videoId='{frags.url}' />
<button onClick= {this.openModal.bind(this)}>Open</button>
</li>
)})
}
</div>
The problem is that each ModalComponent uses the same state property isOpen so when you click on any link it sets this property and each ModalComponent becomes open. You should use unique property for each modal (you can use poperty which you already uses as key).
<li key={frags.id} className="group" id="" style={{width: 'calc(13% - 30px)'}}>
<ModalVideo channel='youtube' isOpen={this.state.isOpen[frags.id]} videoId='{frags.url}' />
<button onClick= {this.openModal.bind(this, frags.id)}>Open</button>
</li>
And your method:
openModal (id) {
this.setState({
isOpen: {
[id]: true
}
});
}
Reason is, you are using single state variable to maintain open/close status of modal, it will work properly for one, but in case of multiple modals, you have to use multiple state values to maintain the statuses, Use this:
Define isOpen as an array in state:
this.state= {
isOpen=[],
}
Use this method to change the status of any particular modal:
openModal(index){
let isOpen = this.state.isOpen.slice();
isOpen[index] = true;
this.setState({isOpen});
}
Bind the index of each modal in onClick method:
render(){
return(
<div>
{(this.props.frag.all == null) ? null :
this.props.frag.all.map((frags, i) => {
return (
<li key={frags.id} className="group" id="" style={{width: 'calc(13% - 30px)'}}>
<ModalVideo channel='youtube' isOpen={this.state.isOpen[i] || false} videoId='{frags.url}' />
<button onClick= {this.openModal.bind(this,i)}>Open</button>
</li>
)})
}
</div>
)
}
Modals in a loop are a bit complicated because they need a unique key for each row. Making the modal work without duplicating it is another challenge. The first mistake many of us make is including the modal within the loop. To prevent the duplication of the modal we need to make it fully dynamically. I have included a full example without having to duplicate the modal inside of the loop. I hope that helps
here is a full example
import React, { Component } from 'react';
import { Button, Alert, Input, Modal, ModalBody, ModalFooter, ModalHeader } from 'reactstrap';
class History extends Component
{
constructor(props)
{
super(props);
this.state = {
userHistory: [{'id': 1, 'test': 'this is a test'}, {'id': 2, 'test': 'this is a test2'}, {'id': 3, 'test': 'this is a test3'}],
showLog: false,
logID: null
}
}
render()
{
const history = this.state.userHistory.map( (ticket, key) =>
{
return (
<tr key={key}>
<td>{ticket.test}</td>
<td>{ticket.id ? <Button color="info" onClick={() => this.setState({logID: ticket.id, showLog: true})}>View</Button> : ''}</td>
</tr>
)
});
return (
<div className="card-body">
<table class="table table-striped">
<thead>
<tr>
<th>test</th>
<th>modal</th>
</tr>
</thead>
<tbody>
{history}
</tbody>
</table>
<Modal id={this.state.logID} isOpen={this.state.showLog} fade="false" toggle={() => this.setState({showLog: false})}>
<ModalHeader toggle={() => this.setState({showLog: false})}>#jerryurenaa is awesome :D</ModalHeader>
<ModalBody>
<p>Modal Number: {this.state.logID}</p>
</ModalBody>
<ModalFooter>
<Button onClick={() => this.setState({showLog: false})}>Cancel</Button>
</ModalFooter>
</Modal>
</div>
);
}
}
export default History;
I'm trying to remove the child element(Note) when a user click the remove button. The remove method is on parent(Board) and I try to pass it to child thru props, but it is not working.
I try to use simple remove, this.remove - not defined remove, or this, this.remove.bind(this) nothing seems to work;location: eachNote(text,i) method
import React from 'react';
import ReactDOM from 'react-dom';
class Note extends React.Component{
constructor(props){
super(props);
this.state = {editing: false};
}
edit() {
this.setState({editing: true});
}
save() {
let val = this.refs.newText.value;
this.setState({editing: false});
}
renderNormal(){
return (
<div>
<p>{this.props.children} </p>
<button type="button" onClick={this.edit.bind(this)}>Edit</button>
<button type="button" onClick={this.hRemove.bind(this)}>Remove</button>
</div>
);
}
renderForm(){
return (
<div>
<textarea ref="newText" defaultValue={this.props.children}></textarea>
<button type="button" onClick={this.save.bind(this)}>Saved</button>
</div>
);
}
render() {
if(this.state.editing ==true ) {return this.renderForm();}
else {return this.renderNormal();}
}
}
class Board extends React.Component{
constructor(props) {
super(props);
this.state = {comments: ['icecream','antimoniu','bentrans'] };
}
remove(i){
let arr = this.state.comments;
arr.splice(i,1);
this.setState({comments: arr});
}
eachNote(text,i) {
return (<Note key={i} index={i} hRemove={this.remove}>{text}</Note>);
}
render(){
return (
<div>
{this.state.comments.map(this.eachNote)}
</div>
);
}
}
ReactDOM.render(<Board />, document.getElementById('container'));
I tried Rafi Ud Daula Refat and Sven (thanks for answers) codes and the below one, but I still received the error: this is undefined;
in the Parent, I have:
eachNote(text,i) {
return (<Note key={i} index={i} hRemove={this.remove.bind(i)}>{text} </Note>);
}
in the Child, I have:
removed(i) {
this.props.hRemove(i);
}
renderNormal(){
return (
<div>
<p>{this.props.children} </p>
<button type="button" onClick= {this.edit.bind(this)}>Edit</button>
<button type="button" onClick= {this.removed.bind(this,i)}>Remove</button>
</div>
);
}
I tried also this.removed.bind(this) and this.removed.bind(i), hRemove={this.remove.bind(i)}, and their combinations not working
If you want to use one method of parent you should pass the function as a props to the child. and from child you can access it as
this.props.functionName
Here in your note Component
<button type="button" onClick={this.hRemove.bind(this)}>Remove</button>
But note component does not have any method named hRemove. It can be assed through
this.props.hRemove()
<button type="button" onClick={this.props.hRemove(idorsomething)}>Remove</button>
And as the function 'remove' in the parent component has one parameter. So from the Child component note you have pass variable a. then it will work. like
this.props.hRemove(id)
When you pass the function remove as a hRemoveprop to your Note, you can find it in this.props.hRemove.
You can also bind the Note directly to the passed remove function:
eachNote(text,i) {
return (<Note key={i} index={i} hRemove={this.remove.bind(i)}>{text}</Note>);
}
Then you simply use
<button type="button" onClick={this.props.hRemove}>Remove</button>
Keep in mind, with ES6 you don't have this in your custom render methods, this can be done in several ways: http://egorsmirnov.me/2015/08/16/react-and-es6-part3.html
I'm new to React and I'm puzzled on something kind of basic.
I need to append a component to the DOM after the DOM is rendered, on a click event.
My initial attempt is as follows, and it doesn't work. But it's the best thing I've thought to try. (Apologies in advance for mixing jQuery with React.)
ParentComponent = class ParentComponent extends React.Component {
constructor () {
this.addChild = this.addChild.bind(this);
}
addChild (event) {
event.preventDefault();
$("#children-pane").append(<ChildComponent/>);
}
render () {
return (
<div className="card calculator">
<p><a href="#" onClick={this.addChild}>Add Another Child Component</a></p>
<div id="children-pane">
<ChildComponent/>
</div>
</div>
);
}
};
Hopefully it's clear what I need to do, and I hope you can help me attain an appropriate solution.
Don't use jQuery to manipulate the DOM when you're using React. React components should render a representation of what they should look like given a certain state; what DOM that translates to is taken care of by React itself.
What you want to do is store the "state which determines what gets rendered" higher up the chain, and pass it down. If you are rendering n children, that state should be "owned" by whatever contains your component. eg:
class AppComponent extends React.Component {
state = {
numChildren: 0
}
render () {
const children = [];
for (var i = 0; i < this.state.numChildren; i += 1) {
children.push(<ChildComponent key={i} number={i} />);
};
return (
<ParentComponent addChild={this.onAddChild}>
{children}
</ParentComponent>
);
}
onAddChild = () => {
this.setState({
numChildren: this.state.numChildren + 1
});
}
}
const ParentComponent = props => (
<div className="card calculator">
<p><a href="#" onClick={props.addChild}>Add Another Child Component</a></p>
<div id="children-pane">
{props.children}
</div>
</div>
);
const ChildComponent = props => <div>{"I am child " + props.number}</div>;
As #Alex McMillan mentioned, use state to dictate what should be rendered in the dom.
In the example below I have an input field and I want to add a second one when the user clicks the button, the onClick event handler calls handleAddSecondInput( ) which changes inputLinkClicked to true. I am using a ternary operator to check for the truthy state, which renders the second input field
class HealthConditions extends React.Component {
constructor(props) {
super(props);
this.state = {
inputLinkClicked: false
}
}
handleAddSecondInput() {
this.setState({
inputLinkClicked: true
})
}
render() {
return(
<main id="wrapper" className="" data-reset-cookie-tab>
<div id="content" role="main">
<div className="inner-block">
<H1Heading title="Tell us about any disabilities, illnesses or ongoing conditions"/>
<InputField label="Name of condition"
InputType="text"
InputId="id-condition"
InputName="condition"
/>
{
this.state.inputLinkClicked?
<InputField label=""
InputType="text"
InputId="id-condition2"
InputName="condition2"
/>
:
<div></div>
}
<button
type="button"
className="make-button-link"
data-add-button=""
href="#"
onClick={this.handleAddSecondInput}
>
Add a condition
</button>
<FormButton buttonLabel="Next"
handleSubmit={this.handleSubmit}
linkto={
this.state.illnessOrDisability === 'true' ?
"/404"
:
"/add-your-details"
}
/>
<BackLink backLink="/add-your-details" />
</div>
</div>
</main>
);
}
}