Show data on buttonclick - reactjs

I am very new to react ,Trying to build a app . The basic functionality i am trying to implement is
I will have some text ,with a button called show details ,clicking the button will show details regarding the text .
I have the text as well as the details saved as a object array in state
Watcher_list :[
{
id:uuid.v4(),
name : "Memory",
showdetails : "False"
},
{
id:uuid.v4(),
name :"Network",
showdetails : "False"
}
]
for the component to render the page the code is as follows
class Watchers extends Component
{
showdetails(id)
{
this.props.showonclick(id);
}
render() {
if (this.props.item.showdetails ==="True") {
return (
<div className="Watchers" >
<li>{this.props.item.name}</li>
<p1>{this.props.item.id} </p1>
<button ref="show details" onClick={this.showdetails.bind(this,this.props.item.id)} >
Show Details
</button>
</div>
);}
else {
return (
<div className="Watchers" >
<li>{this.props.item.name}</li>
<button ref="show details" onClick={this.showdetails.bind(this,this.props.item.id)} >
Show Details
</button>
</div>
);
}
}
}
export default Watchers;
the handler for show click just updates the value of showdetails in the state ,ad upon re rendering the details are displayed.
I just wanted to know wether this is the best way to do this ,or is there a much smarter way that I can do the same thing ?

There are a couple of things that you can improve on,
First: you have a lot of redundant code that can be avoided by using conditional rendering like
class Watchers extends Component {
showdetails(id){
this.props.showonclick(id);
}
render() {
return (
<div className="Watchers" >
<li>{this.props.item.name}</li>
{this.props.item.showdetails ==="True" ? <p1>{this.props.item.id} </p1> : null }
<button ref={(ref) => this.showDetails = ref} onClick =
{this.showdetails.bind(this,this.props.item.id)} > Show Details </button>
</div>
);
}
}
export default Watchers;
Second: bind inside render is not a good pattern since it creates a new function everytime render is called. Check this answer on how to avoid it.
Third: I don't see any point on why you are using ref looking at the code in your question, but even if you need it, you need to use ref callback like ref={(ref) => this.showDetails = ref} instead of string refs since string refs are a legacy

You need not bind inside render function.
No need to set ref
For a change in ID, you do not require separate conditions. You can use the short circuit operator.
class Watchers extends Component {
constructor(props) {
super(props);
this.showdetails = this.showdetails.bind(this);
}
showdetails(id) {
this.props.showonclick(id);
}
render() {
return (
<div className = "Watchers">
<li> {this.props.item.name}</li>
{
this.props.item.showdetails === "True" &&
<p> {this.props.item.id} < /p>
}
<button onClick = {this.showdetails}>Show Details</button>
</div>
);
}
}
export default Watchers;
If you are setting state locally, you should probably set showDetails to Boolean true instead of a String like you have provided.

Related

Why do I get React error "Cannot read propery"

I am getting the "Cannot read property" error when using refs inside a .map function.
What I am trying to do:
Change the background colour of specific values in the .map, so for example if the customer has autorenew on the background colour is green. If not the background colour is red.
The problem I am facing. I can make the code work on a button onClick function but as soon as I put it inside a componentDidMount I get the error cannot read property "style" of undefined.
My code:
export default class Dash extends React.Component {
constructor(props) {
super(props);
this.accordionContent = [];
}
accordionToggle = (value) => {
this.accordionContent[value].style.backgroundColor = "red";
};
render() {
return (
<>
{this.state.valueToMap.map((index, value) => {
var autoRenew = "on";
if (autoRenew === "on") {
this.accordionToggle(value);
}
return (
<div
ref={(ref) => (this.accordionContent[value] = ref)}
className="mini-lower"
>
BACKGROUND I AM TRYING TO CHANGE
</div>
//other bits of code here
);
})}
</>
);
}
}
I can call
<button onClick={() => this.accordionToggle(value)} >Click</button>
inside the .map and it works fine.
I ended up finding a really clean way to do what I was trying to achieve.
Instead of using refs i just used this below
className={this.state.renewalStatus[value] === 'EXPIRED' ? 'class-one' : 'class-two'}
Wish I had worked it out sooner!

React :How to change state inside callback?

As title.
What would I do to change state inside a callback function if I need to change state after I got some results from the back-end or files ,etc...?
Like this:
var strView="";
var CountIsPrime=function(InputNum){
IsPrime(InputNum,function(Res){
strView=Res;
});
};
export class TrialClass extends React.Component{
state={
DisplayString:strView
};
render(){
return <div>
<label>{strView}</label>
<button onclick={()=>CountIsPrime(Math.floor(Math.Random()*10000))}></button>
</div>
}
}
I am wondering how to change the value inside the label when I call CountIsPrime function?
Issues
strView isn't part of any React component state or props, so no amount of updating it will trigger a rerender to display updated values.
onclick might've been a typo, but it isn't valid.
Solution
Move the CountIsPrime callback definition into the component so it can update the state.
Render the state value into the label.
Use onClick handler.
Code
export class TrialClass extends React.Component{
state = {
strView: '',
};
countInPrime = inputNum => IsPrime(
inputNum,
strView => this.setState({ strView }),
);
render() {
const { strView } = this.state;
return (
<div>
<label>{strView}</label>
<button
type="button"
onClick={() => this.countInPrime(Math.floor(Math.Random() * 10000))}
>
X
</button>
</div>
);
}
}

Removing a cookie popup on button click in React isn't working

I've got a very simple component where I'd like to render a 'cookiePopup' based on whether the state 'cookieShow' is true or false. I'd like to switch the state to false by clicking the 'cookie close container', thereby removing the component. This is done through a ternary operator which if 'cookieShow' is true will render the component, if it's false it will return 'null'.
However, whenever the page loads, the cookiePopup doesn't show. I think this is due to my ternary operator, as it feels like this is something straightforward but can't figure it out.
Here's my code:
class CookiePopup extends Component {
constructor(props) {
super(props)
this.state = {
cookieShow: true
}
this.handleClick = this.handleClick.bind(this)
this.showCookiePopup = this.showCookiePopup.bind(this)
this.removeCookiePopup = this.removeCookiePopup.bind(this)
}
handleClick() {
this.setState(state => ({
cookieShow: false
}));
}
showCookiePopup() {
return (
<div className="cookie-popup-container">
<div onClick={this.handleClick} className="cookie-close-container">
<span className="cookie-close-span-1"></span>
<span className="cookie-close-span-2"></span>
</div>
<div className="cookie-text-container">
<p className="cookie-text">By using our website, you agree to our <a class="cookie-link" href="/cookies" target="_self">cookies</a> policy.</p>
</div>
</div>
)
}
removeCookiePopup() {
return (
null
)
}
render() {
return (
<div>
{ this.state.cookieShow ? this.showCookiePopup : this.removeCookiePopup }
</div>
)
}
}
Not sure what I'm doing wrong, but any advice would be appreciated! Thank you.
Try this
{ this.state.cookieShow ? this.showCookiePopup() : this.removeCookiePopup() }
Concerning the render problem, I would say it's because you do not execute the functions in your ternary, try to add the parenthesis like so :
{ this.state.cookieShow ? this.showCookiePopup() : this.removeCookiePopup() }
The following ain't causing your problems, but you could better code wise :
You don't need to bind your methods in the constructor if you use arrow functions, like so :
removeCookiePopup = () => { // TODO }
I'm not sure about the way you wrote your setState, if someone else can confirm it's a valid way to write it ? Anyway, usually we write it this way :
this.setState({ cookieShow: false });
Or even better, you can use the previous value of the state, as React states it's the way to go (https://reactjs.org/docs/state-and-lifecycle.html), like this :
this.setState((previousState) => ({ cookieShow: !previousState.cookieShow}) );
which will toggle your modal between true and false, meaning you only need one method to open and close it.

Is it good practice to add an event in props.children, to set state in the parent, to set state in a child component

I want to build a control like this...
<ClickLabelEdit labelText='sandwich'>
<input type='text' value='sandwich' />
<button onclick={this.closeForm} />
</ClickLabelEdit>
ClickLabelEdit would have two states; show a readonly label and show an editable form for that label. Click the label and it displays the edit form. The edit form details would be defined in a property or as children so we could include any type of markup here. Inside the edit form we will need a hook to tell the control to hide the edit form and display the label again.
This ClickLabelEdit would be a reusable component...used in multiple places around my application. The data used in the label and the form could be taken from a redux store. I think the state (or props) that define whether the label or edit form should be displayed doesn't belong in the redux store because that state/prop would have to be duplicated everywhere I use this control which could become messy.
So...
where to put the showEditForm prop/state
where to put the onclick label function that alters the showEditForm prop/state
how to close the edit form and show the label again (this would be triggered from the edit form)
I have got this to work but my main question is it acceptable to use state to trigger events like I have done below, ie
onclick in children props -> sets state in parent component -> updates state in child
ClickLabelEdit.js
class ClickLabelEdit extends Component {
constructor(props) {
super(props)
this.state = { showChildren: this.props.showChildren }
}
componentWillReceiveProps(nextProps) {
if (nextProps.closeForm === true) {
this.setState({ showChildren: false });
}
}
onLabelClick() {
this.setState({ showChildren: true })
}
render() {
return (
<div>
{
this.state.showChildren ?
this.props.children
:
<div onClick={this.onLabelClick.bind(this)}>
{this.props.labelValue}
</div>
}
</div>
)
}
}
export default ClickLabelEdit
usage
class ManualTester extends Component {
constructor(props) {
super(props)
this.state = { value: 'sandwich', closeForm: false }
}
onSave() {
//persist data
this.setState({ closeForm: true })
}
render() {
return (
<div style={{ width: 250 }}>
<ClickTextEdit labelValue={this.state.value} closeForm={this.state.closeForm}>
<input type='text' value={this.state.value} />
<button onClick={this.onSave.bind(this)}>close</button>
</ClickTextEdit>
</div>
)
}
}
export default ManualTester

React onChange not working as intended

I'm trying to create a products component that get's all the products available on the website and displays each of them in sort of like a box and when the user clicks that box they get redirected to that product page. I'm using react and redux and I'm having a difficulty with onClick. This is how my code looks
class Products extends Component{
constructor(){
super();
this.state = {
products: [...Some array]
};
}
handleProductRedirect(productNumber){
console.log(productNumber)
// Redux function
// this.props.handleRedirect(productNumber)
}
render(){
var products = this.state.products
return (
<div id="content">
{product &&
<div id="searchContent">
{product.map(element => <Item element={element}
handleProductRedirect={this.handleProductRedirect.bind(this)}
key={element['productNumber']}/>)}
</div>
}
</div>
</div>
)
}
};
class Item extends Component{
render(){
var element = this.props.element;
return (
<div id="itemBox" onClick={this.props.handleProductRedirect(element['productNumber'])}>
<h3>{elementTitle.slice(0, 85)}</h3>
<p>{element.Manufacturer}</p>
</div>
)
}
}
so the component gets the products from an api and once it's get them it iterates through them. However, I noticed using chromes developer console that every that it iterates through every <Item /> component it calls handleProductRedirect even though that Item wasn't clicked on. It does it automatically. Instead of calling that function when the div itemBox is clicked on, it calls it when it's rendered. Any suggestions
That's because you are calling the handleProductRedirect on every render for each item. Instead of that, you need send the callback in the onClick prop, something like this:
class Item extends Component{
onClickItem = () => { // <=== Defines the callback and bind it to the instance
const { element } = this.props;
this.props.handleProductRedirect(element['productNumber']);
};
render(){
var element = this.props.element;
return (
<div id="itemBox" onClick={this.onClickItem}>
<h3>{elementTitle.slice(0, 85)}</h3>
<p>{element.Manufacturer}</p>
</div>
)
}
}
This way you are not calling the callback on every render, but when the user actually clicks element.
Also, don't forget to define the propTypes on your components, it really helps to catch issues later on.
Your onClick is calling the function here:
onClick={this.props.handleProductRedirect(element['productNumber'])}>
Instead you should return a function that calls it with the argument. You can do that by making an arrow function like this:
onClick={() => this.props.handleProductRedirect(element['productNumber'])}>
But the best way to do it is to extract it into a class method (so that you don't get unnecessary re-renders):
class Item extends Component {
clickProduct = () => {
this.props.handleProductRedirect(this.props.element['productNumber']);
}
render() {
var element = this.props.element;
return (
<div id="itemBox" onClick={this.clickProduct}>
<h3>{elementTitle.slice(0, 85)}</h3>
<p>{element.Manufacturer}</p>
</div>
);
}
}

Resources