How to onclick method set for only one input? - reactjs

First please click for SS.
Right now I have 2 input which has value credit-card and paypal.
I set an onClick event for CreditCard to provide card informations.It works fine but problem is:
Card details doesn`t disappear when I click paypal input. It works just if I click CreditCart input again. I want to make it disappear even I click paypal input. I mean card details should seem only by clicking Credit Card input.
class CreditCart extends React.Component {
constructor(props) {
super(props);
this.state = {show:false};
this.handleClick = this.handleClick.bind(this);
}
handleClick () {
this.setState({ show : !this.state.show})
}
render () {
return (
//2 input here credir-cart has onClick
{this.state.show && <CreditCart/>
}
Second component which includes cart information part:
class CreditCart extends React.Component {
constructor(props) {
super(props);
}
render () {
// information part
}

Your handleClick method is wrong. The setState method is asynchronous and it will try to execute it in batches which means that the previous value might not be updated yet.
Change it to
handleClick() {
this.setState(function (prevState, props) {
return {
show: !prevState.show
};
});
}
See State Updates May Be Asynchronous

I think something like the following should work for you...
class SomeComponent extends Component {
constructor() {
this.state = {
toggle: false
};
}
render() {
return (
<div>
{this.state.toggle
? <CreditCardComponent />
: <PaypalComponent />}
<button
onClick={e => this.setState({ toggle: !this.state.toggle })}
>
Toggle
</button>
</div>
);
}
}

Related

props in not passing value to Child Component in React

I am developing a simple food app.
Firstly, it will show dishDetails in MenuComponent
and onClick it will pass Id of a selected dish to a function named as
getDish(dishdetail)
Here i want to send props or state to my CartComponent where it will show details of selected Dish.
Problem-1
Props is not passing to Cart (undefined value)
but dishdetail name,id is showing if i do console.log in MenuComponent
How i can pass props/state to Cart kindly guide me.
//Here im binding my function
class Menu extends Component {
constructor(props) {
super(props);
this.getDish = this.getDish.bind(this);
}
//This is my getDish function(in which i want to send props to Cart)
getDish(dishDetail) {
return (
<React.Fragment>
<Cart dishdetail={dishDetail}/> **//undefined in Cart**
{console.log({dishDetail.name})} **//it is working perfectly**
</React.Fragment>
);
}
Working Fine
From Where I am sending data onClick function
<button
onClick={() => this.getDish(this.props.dishes[index])}
></button>
All components should be rendered from render method. And there behaviour can be controlled using state.
// class Menu
constructor(props) {
super(props);
this.state = {
dishDetail: null
};
this.getDish = this.getDish.bind(this);
}
getDish(selectedDish) {
this.setState({
dishDetail: selectedDish
});
}
render() {
return (
<>
<button onClick={() => this.getDish(this.props.dishes[index]])}>Click Me</button>
{/*Cart is called from render and value passed from state*/}
<Cart dishdetail={this.state.dishDetail}/>
</>
);
}
And your cart class will be re-rendered with your new data
class Cart extends React.Component {
constructor(props) {
super(props);
}
render() {
console.log('cart', this.props);
return (
<div>You added {this.props.dishdetail} to cart</div>
);
}
}

Add component on button click

I am making a front end application using typescript and react. I have a component A which amongst other html elements has a textbox. I want to add this component A on click of a button. So if the user clicks the button multiple times, i want a new component A to be created on every click. Also I want to be able to store the text data so that I can later fetch it and process it.
I tried to make a list of this component but it gives me an error.
interface State {
componentList?: ComponentA[];
}
export class ComponentList extends React.Component<Props, State> {
  constructor(props) {
    super(props);
    this.onClick = this.onClick.bind(this);
  }
public onClick(event) {
const componentList = this.state.componentList;
this.setState({
componentList: componentList.concat(<ComponentA key=
{componentList.length} />)
});
}
  public render() {
    return (
      <React.Fragment>
        <button onClick={this.onClick}>Add component</button>
{this.state.componentList.map(function(component, index)
{
return ComponentA
})}
      </React.Fragment>
    );
  }
}
You might want to make two changes in your code.
First initialise your state in the constructor,
constructor(props) {
super(props);
this.onClick = this.onClick.bind(this);
this.state = { componentList: [] }
}
So that react can track that data.
Second thing is, you are returning wrong item from the map in the render function.
Try returning component, which is different copies of <ComponentA ../> that you pushed every time you clicked the button,
public render() {
return (
<React.Fragment>
<button onClick={this.onClick}>Add component</button>
{this.state.componentList.map(function(component, index)
{
return component;
})}
</React.Fragment>
);
}
Keep the component count in the state:
constructor(props) {
super(props);
this.onClick = this.onClick.bind(this);
this.state = {
numComponents: 0
}
}
Add a new function which creates an array of the component for rendering later:
clickedComponents = () => {
let componentArray = [];
for (let i=0; i<this.state.numComponents; i++) {
componentArrays.push(<ComponentA />);
}
return componentArray;
}
Increment the component count with your onClick function:
public onClick(event) {
this.setState({numComponents: this.state.numComponents + 1});
}
Render the component array:
public render() {
return (
<React.Fragment>
<button onClick={this.onClick}>Add component</button>
{this.clickedComponents()}
</React.Fragment>
);
}

Child not updating after Parent State changed

I am quite new with React and I have problem bellow
I have a parent component like this:
class Parent extends React.Component {
constructor(props) {
super(props);
this.state = {count:1};
}
shouldComponentUpdate(nextProps, nextState, nextContext) {
return false;
}
setCount = () => {
this.setState({
count: 2
});
};
render() {
const {name, running, onRun, onStop} = this.props;
return (
<div>
<Test count={this.state.count}/>
<p><a href="#" onClick={this.setCount}>SetCount</a></p>
</div>
);
}
}
And here is Test component
class Test extends React.Component {
constructor(props) {
super(props);
}
shouldComponentUpdate(nextProps, nextState, nextContext) {
return true;
}
render() {
const {count} = this.props;
return (
<div>
{console.log("Counting")}
<p>{count}</p>
</div>
);
}
}
I have method "shouldComponentUpdate" returns "false" in Parent component because I don't want to re-render it.
My understanding is React know which part of DOM need to be re-rendered. And in this case, the state of Parent changes will re-render "Test" component
But when I run above code, "Test" component does not redender.
Is there anything wrong in my code?
Thanks a lot for your help
You need to return true from your parent's shouldComponentUpdate method.
If you return false, after the initial render it won't update, even if you call a function that calls setState.
Is the refresh of the whole page are you talking about? If thats the case, probably you wanna change your <a> tag to button or use e.preventDefault();.
If not, I am not sure if that is possible. If you setState in the parent, it will rerender parent as well as the children. If you dont want to render the parent then you have to manage individual state management in the child level.
For example,
class Parent extends React.Component {
constructor(props) {
super(props);
}
render() {
const {name, running, onRun, onStop} = this.props;
return (
<div>
<Test/>
</div>
);
}
}
class Test extends React.Component {
constructor(props) {
super(props);
this.state = {count:1};
}
setCount = (e) => {
e.preventDefault();
this.setState({
count: 2
});
};
render() {
const {count} = this.state;
return (
<div>
{console.log("Counting")}
<p>{count}</p>
<p><a href="#" onClick={this.setCount}>SetCount</a></p>
</div>
);
}
}

Pass data through levels - open Modal dialog at the end

I've been pounding my head on this for many hours and could use some help.
Fundamentally, what I am trying to do is that I have two layers of components - the last of which is supposed to open up a Modal Dialog when you click it. Because of React's idea that a component should only change its own state, I want to propagate that data up and set that variable.
Here is the layout. The FirstLevel.jsx file is the top of my hierarchy. It is followed by SecondLevel.jsx and ThirdLevel.jsx which is where the actual text is clicked.
I don't know about the syntax on anything. Not sure if onUserInput is the right attribute to use or handleUserClick is a built-in thing or a user-defined thing. The idea here is that I am trying to propagate the callback function handleUserClick down into the SecondLevel. Is this right so far?
FirstLevel.jsx
export default class FirstLevel extends React.Component {
constructor(props) {
super(props);
this.state = {
dialogActive: ''
};
this.handleUserClick = this.handleUserClick.bind(this);
}
handleUserClick(dialogActive) {
this.setState({
dialogActive: dialogActive
});
}
render() {
<SecondLevel onUserInput={this.handleUserClick}/>
}
Now, on the SecondLevel, I propagate the callback function even further down into the ThirdLevel. Is this the right way to do it so far?
SecondLevel.jsx
render () {
//other logic and tags before this
<ThirdLevel onUserInput={this.props.onUserInput}/>
}
Now this level is where all hell breaks loose and I have no idea what I am doing. On the click, I want to set the dialogActive variable that was propagated down and then let that float back up. I still don't know if onUserInput is the right thing to do or if the parameter is even correct. Everything is very hazy because it was just gotten by following tutorials and doing lots of Googling and throwing in bits and pieces from everywhere.
ThirdLevel.jsx
export default class ThirdLevel extends React.Component {
constructor(props) {
super(props);
this.handleClick = this.handleClick.bind(this);
}
handleClick() {
this.props.onUserInput(
this.dialogActive.value
);
//show Modal dialog somehow
}
render() {
return <text ref={(input) => this.dialogActive = true} onClick={this.handleClick}> {this.props.value}</text>;
}
Finally, I want to show some modal dialog. Clicking the text needs to reveal a modal dialog. The modal dialog is in another component called MyModal.jsx
In the ThirdLevel, I've tried importing MyModal and tried calling the showModal function. Didn't work. Then, I tried doing some React.createElement(MyModal) stuff and rendering it but that didn't work. All kind of other things that I forgot and just trying stuff until it works but it didn't. What am I doing wrong?
MyModal.jsx
export default class MyModal extends React.Component {
constructor(props) {
super(props);
this.state = {show: false};
this.showModal = this.showModal.bind(this);
this.hideModal = this.hideModal.bind(this);
}
showModal() {
this.setState({show: true});
}
hideModal() {
this.setState({show: false});
}
render() {
return (
<Modal
{...this.props}
show={this.state.show}
onHide={this.hideModal}
dialogClassName={styles.largeDialogBox}
>
//more modal stuff here
);
}
}
Big picture: trying to propagate a click action back up to the top of the hierarchy to set some state and that click action needs to open a modal dialog. Any help would be greatly appreciated.
Edit
Do I do something like this in my ThirdLevel?
handleClick() {
this.props.onUserInput(
this.dialogActive.value
);
//show Modal dialog somehow
var newmodal = new MyModal(this.props);
React.render(React.createElement(newModal));
}
render() {
return <text onClick={this.handleClick}> {this.props.value}</text>;
}
Edit 2
My ThirdLevel render function returns this:
<div>
<MyModal isDialogActive={this.props.dialogActive} onHideModal={this.props.onUserInput}/>
<tspan onClick={this.handleClick}> {this.props.value} </tspan>
</div>
When that gets passed back up into the SecondLevel, it becomes:
<text>
<div>
<MyModal isDialogActive={this.props.dialogActive} onHideModal={this.props.onUserInput}/>
<tspan onClick={this.handleClick}> {this.props.value} </tspan>
</div>
</text>
It's weird to wrap things in the div but that's the only way to make the render work. Even though the resulting DOM has all the tags there, none of the actual tspans are showing.
I believe this will get you on the right path.
I would suggest refactoring the name of some of your functions as it does get a bit confusing. handleUserClick then onUserInput etc. But you've already mentioned that in your OP.
// First Level
export default class FirstLevel extends React.Component {
constructor(props) {
super(props);
this.state = {
dialogActive: false
};
this.handleUserClick = this.handleUserClick.bind(this);
}
handleUserClick(dialogActive) {
this.setState({
dialogActive: dialogActive
});
}
render() {
<SecondLevel onUserInput={this.handleUserClick}/>
}
}
// Second Level
...
render () {
//other logic and tags before this
<ThirdLevel onUserInput={this.props.onUserInput}/>
}
...
// Third Level
export default class ThirdLevel extends React.Component {
constructor(props) {
super(props);
this.handleClick = this.handleClick.bind(this);
}
handleClick() {
this.props.onUserInput(true);
}
render() {
return (
<div>
<MyModal isDialogActive={this.props.dialogActive} onHideModal={this.props.onUserInput} />
<button onClick={this.handleClick}>Show Modal</button>
</div>
)
}
}
// Modal
export default class MyModal extends React.Component {
constructor(props) {
super(props);
this.hideModal = this.hideModal.bind(this);
}
hideModal() {
this.props.onUserInput(false);
}
render() {
return (
<Modal
{...this.props}
show={this.props.isDialogActive}
onHide={this.hideModal}
dialogClassName={styles.largeDialogBox}
>
//more modal stuff here
);
}
}
However I would be asking why you need this logic in the FirstLevel and it cannot be further down the component tree.

Binding event handlers in React Components fires onClick when rendered

I am learning React. In a test app I'm writing, I am rendering some buttons with onClick methods. When they are rendered like this, they work and call the selectMode function as expected when clicked.
constructor(props) {
super(props);
this.state = { mode: 'commits', commits: [], forks: [], pulls: [] };
}
...
selectMode(mode) {
this.setState({ mode });
}
render() {
...
return (<div>
<button onClick={this.selectMode.bind(this, 'commits')}>Show Commits</button><br/>
<button onClick={this.selectMode.bind(this, 'forks')}>Show Forks</button><br/>
<button onClick={this.selectMode.bind(this, 'pulls')}>Show Pulls</button>
</div>
)
}
But when I tried the suggested best practices way shown below, by binding in the constructor, the selectMode function is called three times when the component is rendered. Why are the onClick event handlers being called then? What do I have wrong?
constructor(props) {
super(props);
this.state = { mode: 'commits', commits: [], forks: [], pulls: [] };
this.selectMode = this.selectMode.bind(this)
}
...
selectMode(mode) {
this.setState({ mode });
}
render() {
...
return (<div>
<button onClick={this.selectMode('commits')}>Show Commits</button><br/>
<button onClick={this.selectMode('forks')}>Show Forks</button><br/>
<button onClick={this.selectMode('pulls')}>Show Pulls</button>
</div>
)
}
your this.selectMode(...) is executed IMMEDIATELY whenever your component is rendered.
<.. onClick={this.selectMode('commits')}..../> <-- function is called immediately
You can use arrow function to create an anonymous function in which you can call your method. In this way, you this.selectMode method only get called when the click event occurs :
<.. onClick={() => this.selectMode('commits')}..../>
If you don't want to create anonymous functions everytime you render the component, you can store an value to an attribute of the element. Like this :
constructor(props) {
super(props);
this.state = { mode: 'commits', commits: [], forks: [], pulls: [] };
this.selectMode = this.selectMode.bind(this)
}
selectMode(event){
this.setState({mode: e.target.name});
}
render(){
....
<.. onClick={this.selectMode} name='commits' ..../>
..}
I'm not sure, but I think it's because you call upon each onClick function by adding the parentheses. If you use ES6 you could try doing this:
onClick = () => { this.selectMode('commits') }
onClick = () => { this.selectMode('forks') }
onClick = () => { this.selectMode('pulls') }

Resources