ag-grid react cellRendererFramework doesn't re render when state changed - reactjs

I'm displaying data in ag-grid-react and grid has come conditional cell rendering based on state. Here is my code. This is working on first run and displaying "YEAH" button. when i clicked button i want to change with NOOO button
This is state
this.changebutton = this.changebutton.bind(this);
this.state= {
isyes = "yes"
}
This is ag-grid-cell-renderer
cellRendererFramework: (params) => {
return <div>
{
this.state.isyes === "yes" ?
<button onClick= {() => this.changebutton()}>YEAH</button>
:
<button onClick= {() => this.changebutton()}>NOOOO</button>
}
</div>
}
this is state changer
changebutton() {
this.setState({isyes: "no" })
console.log(this.state.isyes)
}
I seeing state is changing properly, But doesn't see any change of button. why?

Your code seems incomplete to check your situation. First thing that comes in mind is the expression is evaluated once (maybe, as it appears as a method of an object not directly in render function) and is never retriggered.
Also notiche that setState() is async so you should not:
this.setState({isyes: "no" });
console.log(this.state.isyes);
instead you should:
this.setState(
{isyes: "no" },
() => console.log(this.state.isyes);
);
Try with:
api.refreshCells()
ref: ag-grid.com/javascript-grid-cell-rendering-components

Related

How to handle onClick event to display text after the click

Using react typescript and I’m confused that when I click a button I want some text to appear below the button or at-least anywhere so I made a function to handle the onClick from the button and returned a h1 from the function but turns out no h1 appears on screen after button click. Any idea why?
const handleOnClick=(id:any)=>{
console.log("button clicked" + id)
return(
<h1>Clicked it</h1>
);
}
My Function is this and in another function I have
<button onClick={()=>{handleOnClick(someId)}}>a</button>
I can see the console log but the h1 doesn’t work. Any ideas?
If you think about it, what your handleOnClick doing is returning a bunch of jsx, where do you think these jsx will appear since we didn't specify any location for them? Now if you try something like this:
<button>{ handleOnClick('someId') }</button>
You will see the h1 on the screen because you specify that's where you want to render it, right inside the button element.
A classic way in js to render out something on button click is like this:
const handleOnClick=(e)=>{
// create the element
const newEle = document.createElement('h1');
newEle.innerText = 'Hello';
// append it inside the button
e.target.appendChild(newEle);
}
export default function App() {
const [nameId, setNameId] = useState<String>("");
const handleClick = (id: String) => () => {
console.log("button clicked: ", id);
setNameId(nameId ? "" : id);
};
return (
<>
<button onClick={handleClick("Rohan")}>
{nameId ? "Hide" : "Greet"}
</button>
{!!nameId && <h1>Hello {nameId} Haldiya</h1>}
</>
);
}
When the click is triggered, you need to add the <h1> element into your JSX code, and returning it from the click handler is not enough because you need to tell it where is should be added.
A good way of doing that in React is by using a state which tells you if the button was clicked or not, and if it was, then you display the <h1> element onto the screen. See the code below:
const [isActive, setIsActive] = useState(false);
const handleOnClick = (id) => {
console.log("button clicked" + id);
setIsActive(true);
};
and in your JSX code, below the button, you just need to add the second line of the following:
<button onClick={()=>{handleOnClick(someId)}}>a</button>
{isActive && <h1>Button was clicked.</h1>}
And if you want to toggle the click, So the first time you click the <h1> is showing , but if you click again it disappears, then you could simply do this in the handleOnClick function instead of the above:
const handleOnClick = (id) => {
console.log("button clicked" + id);
setIsActive((prevState) => (prevState === false ? true : false));
};
Hope this helps!

Problem with useEffect() on loading the page

I am having trouble with my react quiz app. Here follows the description:
This is from App.js file:
...
const [createQuiz, setCreateQuiz] = useState(false);
...
useEffect(()=> {
const reRender = () => {
setCreateQuiz(true)
}
window.onload=function(){
document.getElementById("myBtn").addEventListener("click", reRender);
}
// return document.getElementById("myBtn").removeEventListener("click", reRender);
}, [createQuiz])
return (
<QuizContextProvider>
{
(createQuiz) ? (
<div>Form</div>
) : (
<div>
<Modal/>
<Question question={questions[questionNumber]} next={goToTheNext} />
</div>
)
}
{console.log(createQuiz)}
</QuizContextProvider>
);
}
As can be seen it is a conditional rendering: a Modal window asks a user whether they want to take the existing quiz or create their own and when the user clicks "Create your own " button, the app should re-render over again, this time the useEffect() (in App.js) sets the value of createQuiz to true. the code excerpt below is from <Modal /> component:
return (
<div className='benclosing' style={{display:displayValue}}>
<div className='modal'>
<h1>Welcome!</h1>
<p>Do you want to take an existing quiz or create your own?</p>
<button onClick={hideWindow} >Existing quiz</button>
<button id='myBtn'>Create your own</button>
</div>
</div>
)
}
Everthing works fine as expected, except for 1: whenever reload icon is clicked, my page re-renders over-again and the user is again asked if they want to take the existing quiz. I want that refreshing affect nothing. I am stuck with this problem. How can I achieve the desired result?
I also tried this:
const reRender = () => {
setCreateQuiz(true)
}
useEffect(()=> {
reRender()
//return setCreateQuiz(false)
}, [createQuiz])
It didn't work as expected. I described what it caused in my 2nd comment to Red Baron, please have a look.
The proper way to achieve what you want is to create an event handler inside your App component that will set createQuiz to true when the Create your own button gets clicked inside the Modal component.
function App() {
const [createQuiz, setCreateQuiz] = React.useState(false);
const handleShowQuizForm = () => {
setCreateQuiz(true);
};
return (
<div>
{createQuiz ? (
<div>Form</div>
) : (
<>
<Modal showQuizForm={handleShowQuizForm} />
</>
)}
</div>
);
}
function Modal(props) {
return (
<div>
<button type="button" onClick={props.showQuizForm}>
Create your own
</button>
</div>
);
}
Here's an example:
CodeSandbox
There's no need for the useEffect hook here and the window.onload event implies to me that you'd want to set createQuiz to true then "refresh" your page and expect createQuiz to now be true - it won't work like that.
Additionally, the way you're using the useEffect hook could be problematic - you should try to stay away from updating a piece of state inside of a useEffect hook that's also part of the dependency array:
React.useEffect(() => {
const reRender = () => {
setCreateQuiz(true);
}
// although not an issue here, but if this hook was
// rewritten and looked like the following, it would
// case an infinite re-render and eventually crash your app
setCreateQuiz(!createQuiz);
}, [createQuiz]);

How to Toggle Multiple Hide/Display state

I am having trouble coming up with a method that allows me to toggle multiple displays on and off without hard-coding a function for each displayed component.
I was wondering if there is a way to toggle these display states with one function kind of like and onChange event.
this.state = {
showOne: false,
showTwo: false
}
display = () => {
let { name, value } = e.target
this.setState({ [name]: !value })
}
return(
<button name={showOne} value={this.state.showOne} onClick={this.display}>
{!showOne
? (
null
) : (
<div><ComponentOne/></div>
)}
</button
<button name={showtwo} value={this.state.showTwo} onClick={this.display}>
</button
{!showTwo
? (
null
) : (
<div><ComponentTwo/></div>
)}
this only somewhat works the issue is that it changes the state to a string instead of a Boolean. aka showOne: false => showOne: 'false'.
I know the name and value properties are for inputs but I was wondering if there was something similar along these lines so I can have one function that allows the displaying/un-displaying of multiple components.
You can set them up with an inline function that passes in an identifier, which is then used as a key in the state:
onClick={() => toggle(itemName)}
toggle = (itemName) => {
this.setState({ [itemName]: !this.state[itemName] });
}
Another option is to define a component for the button that takes name and onClick props, and have the component’s internal onClick invoke the prop onClick with the name. (It’s hard to write example code from the phone. I can update this if it isn’t clear.)
toggleDisplay = (name) => {
this.setState({
[name]: !this.state[name]
})
}
onClick={()=> this.toggleDisplay('showOne')}
Worked Tyvm

onClick method runs setState multiple times in a row, negating intended purpose

I'm attempting to have my onClick method handleClick set the state of the active and groupCardInfo properties. active in particular is a boolean, and I'm using this bool value to determine whether a side menu item should be expanded or not.
SideMenuContainer component that calls handleClick:
render() {
if (this.props.active == true){
return (
<ParentContainer>
<p onClick={this.props.handleClick(this.props.properties)}>{this.props.parentName}</p>
<NestedContainer>
{this.props.properties.map(propertyElement => {
return (
<NestedProperty onClick={() => { this.props.changeInfoList(propertyElement.name, propertyElement.data_type, propertyElement.app_keys)}} >
{propertyElement.name}
</NestedProperty>
);
})}
</NestedContainer>
</ParentContainer>
);
}
The issue is that clicking that <p> results in handleClick running multiple times. so rather than toggling the active value from false to true, it toggles it back and forth multiple times so that it goes from false back to false again.
What's incorrect with the way I'm structuring this method in the parent App.js that's causing this?:
handleClick(properties){
console.log("toggle click!")
// this.setState({active : !this.state.active});
this.setState({
active: !this.state.active,
groupedCardInfo: properties
})
console.log("the active state is now set to: " + this.state.active)
}
It's because you are invoking the function in event handler. The first time render runs it will execute your event handler. You can do this like your other onClick handler:
<p onClick={() => { this.props.handleClick(this.props.properties) }}>{this.props.parentName}</p>
Or you can do it like this:
<p onClick={this.props.handleClick}>{this.props.parentName}</p>
But then you would have to change how you reference properties in your click handler. Like this:
handleClick(){
const properties = this.props.properties
this.setState({
active: !this.state.active,
groupedCardInfo: properties
})
console.log("the active state is now set to: " + this.state.active)
}
Try using arrow function, like you did on the other onClick:
<p onClick={() => this.props.handleClick(this.props.properties)}>
Calling this.props.handleClick... while rendering, invokes it.
Then, setting the state, makes the component to re render.

Toggling font awesome Icon using React

I want to toggle a fontAwesome icon class name on click. When clicked, the icon should change the color and also call a service which adds an object to a favorite list in the server (hence, why I use e.currentTarget: I need to remember which icon was clicked). This code works on the first click, but fails to change the class back on the second click (doing an inspect, it says the FA´s classname equals "Object object"). Any idea how I could fix it?
<FontAwesomeIcon onClick={this.ToggleClass} size={"sm"} icon={faHeart} />
ToggleClass = (e) => {
const heart = {
color: "#E4002B",
}
const clicked = {
color: "#E4002B",
background: "red"
}
if (e.currentTarget.className.baseVal != heart && e.currentTarget.className.baseVal != clicked) {
return e.currentTarget.className.baseVal === clicked;
#Callservicehere
}
else if (e.currentTarget.className.baseVal === clicked) {
e.currentTarget.className.baseVal = heart;
#callservicehere
}
}
You're not thinking in React yet :)
Accessing the event target and imperatively manipulating the DOM bypasses React's rendering - you might as well just be using jQuery. Not that there's anything bad about that, but it's not the right way to go about things in React.
In React, if you need to change the DOM in response to user interaction you do it in the render method, i.e. output different JSX based on the component's current state or props.
A couple things that might help here:
clicked and heart are both objects which means that you cannot compare them without using a deep comparison method.
var a = { id: 1 }
var b = { id: 1 }
console.log(a == b) //false
console.log(a === b) //false
If you want to compare them, you can convert them both to strings using the toString() method
heart.toString() === clicked.toString()
In your first if condition, it looks like you're returning a true/false value instead of assigning a desired classname to your target.
return e.currentTarget.className.baseVal === clicked // true/false
e.currentTarget.className.baseVal = clicked // assigned
You could also take the approach of keeping your classnames as strings and adding your styled objects inside of css
class MysteryComponent extends React.Component {
state = {
className: 'heart'
}
toggleClass = (e) => {
if (this.state.className === 'heart') {
this.setState({ className: 'clicked' })
} else if (this.state.className === 'clicked') {
this.setState({ className: 'heart' })
}
}
render() {
return (
<div className={this.state.className}>
<FontAwesomeIcon onClick={this.toggleClass} size={"sm"} icon={faHeart} />
</div>
)
}
}
// css
.heart {
color: "#E4002B";
}
.clicked {
color: "#E4002B";
background: "red";
}
I see. You want to fill/unfill the color of the heart as the user clicks. The reason why the results are not meeting your expectations is because of the event.targets are especially funky with FontAwesome. You may think you're clicking on it, but it manipulates the DOM in a way that when you try extract the className, it's value is often inconsistent.
This is why everyone is recommending that you make use of React's state. The logic that determines how elements are styled is now more controlled by the component itself instead of the FontAwesome library. Consider the code below, we only care about whether the item was clicked, not what class it initially has.
class Example extends React.Component{
state = {
clicked: false
}
handleOnCLick = () => {
this.setState({
clicked: !this.state.clicked
})
}
render(){
var clicked = this.state.clicked
return(
<button onClick={this.handleOnClick}>
<i
class={ clicked ? "fas fa-heart" : "fas fa-circle"}
</i>
</button>
)
}
}

Resources