I am working with a form in react, and what I would like is that when I click a button, I add a new component which is just an input to the screen. It all mostly works, as planned. The issue is with the following: the layout is that I have one main component, which then displays a child component. That child component is called from a map of a useState. (More after code snippet)
This is the code of the main component:
import React, { useState } from "react";
import SingleProfile from "./individual_profile";
const ProfileInformation = (props) => {
console.log("proflie render");
const [ProfilesBoolean, setProfilesBoolean] = useState(false);
const [profiles, setProfiles] = useState(props.Data['profiles'])
const FieldAdd = (event)=>{
event.preventDefault();
const copy = profiles;
copy.push({Network:'',url:''})
return(copy)
}
function CreateInput(){
return profiles.map((data, index) =><SingleProfile index={index} data={data} />)
}
const accordion = (event) => {
const NextElement = event.target.nextElementSibling;
if (!event.target.className.includes("display")) {
NextElement.style.maxHeight = NextElement.scrollHeight + "px";
} else {
NextElement.style.maxHeight = 0;
}
};
return (
<div className="AccordionItem">
<div
className={
ProfilesBoolean ? "AccordionHeader-display" : "AccordionHeader"
}
onClick={(e) => setProfilesBoolean(!ProfilesBoolean)}
id="ProfileForm"
>
Profiles
</div>
<div className="AccordionContent">
<div className="AccordionBody">
{
profiles.map((data, index) => (
<SingleProfile index={index} data={data} />
))
}
<button id="ProfileAdd" onClick={(e) => {setProfiles(FieldAdd(e))}}>
Add a profile
</button>
</div>
</div>
</div>
);
};
export default ProfileInformation;
When I click the button and onClick fires FieldAdd() the useState updates, with a new empty object as expected. However, it does not appear inside my <div className="AccordionBody"> as I would expect it to.
The following code is used to display components, by opening and closing the child div. When it is open is when you see the child components and the add button. If I click the div, to close and then click again to re-open it, the new child component appears.
<div
className={ProfilesBoolean ? "AccordionHeader-display" : "AccordionHeader"}
onClick={(e) => setProfilesBoolean(!ProfilesBoolean)}
id="ProfileForm"
>
Profiles
</div>;
Is it possible to have the child component appear without having to close and re-open the div?
Your clickHandler FieldAdd is incorrect. You are mutating the state directly which will not cause re-render.
use setProfiles to update the state in the clickHandler. Like this
const FieldAdd = (event)=>{
setProfiles(prev => [...prev, {Network:'',url:''}])
}
Trigger the onClick like this
<button id="ProfileAdd" onClick={(e) => {FieldAdd(e)}}>
Add a profile
</button>
...
Related
For example I have this code.
And I want to use CSS transitionfor Button when showButton and when !showButton. Now it's just removed and add Button when showButton changes.
{showButton && (
<Button
onClick={() => setShowMessage(true)}
size="lg"
>
Show Message
</Button>
)}
Is it possible make by some events or appending classNames like active?
Append the className with the ternary operator.
But, for example, this code will only adjust the class of the button specified (effectively doing the same thing you described, hiding & showing the button):
import React, { useState } from 'react';
export const Component = () => {
const [showButton, setShowButton] = useState(false);
const handleClick = () => {
setShowButton(true);
}
return (
<button
onClick={handleClick}
className={showButton ? 'showButtonClass' : 'hideButtonClass'}
>
Show Message
</button>
);
};
For content to show once the button is clicked, you'll need something like:
import React, { useState } from 'react';
export const Component = () => {
const [showMessage, setShowMessage] = useState(false);
const handleClick = () => {
setShowMessage(true);
}
return (
<div>
<button
onClick={handleClick}
>
Show Message
</button>
{showMessage && <h1>
The message you'll see when clicking!
</h1>}
</div>
);
};
So basically I want when I press on the Button I want to Icon to disappear as well as Button. I have tried something but clearly, it is not working, so I would appreciate some help if possible
const Button = (props) => {
const[toggleIcon, setToggleIcon]=React.useState('true')
function Icon() {
toggleIcon(false)
}
return(
<div>
<button onClick={()=> setToggleIcon('false')}></button>
</div>
)
}
export default Button
const Icon = (props) => {
return(
<div>
<Icon>{props.Icon('false')}</Icon>
</div>
export default Icon
You can prevent a component from rendering by returning null. Use conditional rendering with the toggleIcon state as the condition. The onClick handler changes the state.
E.G.:
const Button = () => {
const [toggleIcon, setToggleIcon] = React.useState(true)
return toggleIcon ? (
<button onClick={() => setToggleIcon(false)}><Icon /></button>
) : null
}
export default Button
Take a look at the Conditional Rendering section of the React docs.
I have created an Accordion component which has data(object) and expanded(boolean) as props.
expanded props is used to set the expanded/collapsed state of this component passed as a prop.
const DeltaAccordion = ({ index, data, expanded = true }) => {
Accordion component also has an internal state
const [isExpanded, setIsExpanded] = useState(expanded);
which is used for expanding/collapsing the accordion.
Below is my complete component
Accordion.jsx
import React, { useState } from "react";
// styles
import styles from "./index.module.scss";
const Accordion = ({ index, data, expanded = true }) => {
// state
const [isExpanded, setIsExpanded] = useState(expanded);
console.log(data.name, `prop-val==${expanded}`, `inner-state==${isExpanded}`);
return (
<div
className={`${styles.container} caption ${isExpanded && styles.expanded}`}
>
<div className={styles.header} onClick={() => setIsExpanded(!isExpanded)}>
<div>{data.name}</div>
<div>Click</div>
</div>
<div className={styles.content}>
{data.newValue && (
<div className={styles.newValue}>
<span>{data.newValue}</span>
</div>
)}
{data.oldValue && (
<div className={styles.oldValue}>
<span>{data.oldValue}</span>
</div>
)}
</div>
</div>
);
};
export default Accordion;
The parent component has a list of data and it loops through the list to create an accordion for each data item.
App.js file
{dataList.map((data, index) => (
<Accordion data={data} expanded={!collpaseAll} index={1} />
))}
Here goes problem
There is also a button in my App.js file which is for either expanding/collapsing all the accordions together.
But when I pass its value as prop expanded to the accordion component then this value is not getting applied to the internal isExpanded state of the accordion component.
Here is live running code at codesandbox: https://codesandbox.io/s/goofy-nobel-qfxm1?file=/src/App.js:635-745
Inside the Accordion
const [isExpanded, setIsExpanded] = useState(expanded);
This line will take first time(on first render) value. To assign it next time(rerender) value you need to add a effect
useEffect(() => {
setIsExpanded(expanded);
}, [expanded]);
And in your case, you can use the props expanded directly inside Accordion, you dont need to take it in local state.
I am trying to create an accordion component using React, but the animation is not working.
The basic idea is, I believe, pretty standard, I am giving each item body a max-height of 0 which is affected by adding a show class to an element. I am able to select and show the item I want, but the animation to slide in/out is not working.
With the Chrome dev tools open, when I click on one of the items I can see that the whole "accordion" element is flashing, which leads me to believe that the whole element is being re-rendered. But I am unsure why this would be the case.
Here is the relevant Accordion component:
import React, { useState } from "react";
const Accordion = ({ items }) => {
const [selectedItem, setSelectedItem] = useState(0);
const AccordionItem = ({ item, index }) => {
const isOpen = index === selectedItem;
return (
<div className="accordion-item">
<div
onClick={() => {
setSelectedItem(index);
}}
className="accordion-header"
>
<div>{item.heading}</div>
</div>
<div className={`accordion-body ${isOpen ? "show" : ""}`}>
<div className="accordion-content">{item.body}</div>
</div>
</div>
);
};
return (
<div className="accordion">
{items.map((item, i) => {
return <AccordionItem key={i} item={item} index={i} />;
})}
</div>
);
};
export default Accordion;
And here is a codepen illustrating the problem:
https://codesandbox.io/s/heuristic-heyrovsky-xgcbe
of course, its going to re-render. When ever you call setSelectedIem, state changes and hence react re-renders on state change to exhibit that change.
Now if you place this
const [selectedItem, setSelectedItem] = useState(0);
inside Accordion Item, it would just re-render accordion item, but would mess up your functionality.
I want to render child component when I click the button
Parents
const Parents:React.FC<PropTypes> = ({inputs}) => {
return(
<div>
<Button onClick={() => <Child props={inputs}/>}
</div>
)}
Child
const Child:React.FC<AnotherPropTypes> = ({props}) => {
// ...
}
I am using React, TypeScript and Material-UI for it.
My question is that, it does not seem like onClick event trigger Child component. How can I run child component when I click the button?
Any help appreciated!
Add state to your component. The click event sets the state, and the state is used to decide whether or not to render extra things.
const Parents:React.FC<PropTypes> = ({inputs}) => {
const [showChild, setShowChild] = useState(false);
return(
<div>
<Button onClick={() => setShowChild(true)} />
{showChild && <Child props={inputs}/>}
</div>
)
}