So basically I want to hide a Button when I press on it.
const Button=()=>{
const [hideButton, setHideButton]= React.useState(false)
function Button(){
setHideButton(false)
}
return(
<div>
<button onClick={setHideButton}> </button>
</div>
)
}
Initially make it true to show the button. hide it based on click.
const Button=()=>{
const [hideButton, setHideButton]= React.useState(true)
function handleClick(){
setHideButton(false)
}
return(
<div>
{hideButton && <button onClick={handleClick}>Click</button>}
</div>
)}
Or just put !(Not) before hideButton -> !hideButton and make it true on button click.
const Button=()=>{
const [hideButton, setHideButton]= React.useState(false)
function handleClick(){
setHideButton(true)
}
return(
<div>
{!hideButton && <button onClick={handleClick}>Click</button>}
<ChildComponent hideButton={hideButton} handleClick={handleClick}/>
</div>
)}
function ChildComponent ({hideButton, handleClick}) {
return (
<>
<button onClick={handleClick}>Child Button</button>
{!hideButton && <p>
This is content/paragraph which will be hidden
based on based on button click from parent component.
</p>}
</>
)
}
You can change as per your requirement i have passed function and state variable to child component. child button will toggle your content of child component and parent button hide itself once clicked.
Just conditionally render the button so it doesn't render if hideButton is true:
return(
<div>
{!hideButton && <button onClick={setHideButton}> </button>}
</div>
)
It is needed to use React hook for doing this.
const Button=()=>{
const [hideButton, setHideButton]= React.useState(false)
function hide(){
setHideButton(false)
}
return(
<div>
{!hideButton && <button onClick={hide}> </button>}
</div>
)
}
There are several things going wrong here.
This is the way your code should look (one of many ways)
const Button = () => {
const [hideButton, setHideButton] = React.useState(false)
function handleHideButton() {
setHideButton(true)
}
return (
<div>
{!hideButton ? <button onClick={handleHideButton}>Hide</button> : null}
</div>
)
}
What you were doing wrong:
const Button=()=>{
const [hideButton, setHideButton]= React.useState(false)
// You're creating this function called Button, but should be called something esle like handleHideButton
function Button(){
// You should be setting this to true when the button is clicked.
setHideButton(false)
}
return(
<div>
// here you should be calling the handleHideButton function defined above. Calling setHideButton isn't a good option here.
<button onClick={setHideButton}> </button>
</div>
)
}
Related
React newbie here,
I have a component for login which has two states (logged and not), and a button, a button click makes a form appear for the user.
function Main() {
const [state, setState] = useState(false);
function buttonClick(event) {
state = !state;
}
return (
<div id='main'>
<p id='main-body'>{lorem}</p>
{ state ? <LoginForm /> : <LoginButton />}
</div>
)
}
my issue is that the onClick itself (and the button) are in another component:
function LoginButton() {
return (
<div class="btn-centered">
<button type="button" class="btn btn-outline-primary btn-lg btn-centered" onClick={}>Login</button>
</div>
)
}
Have I understood the concept wrongly? How do I achieve the result I want with the outside component?
Thanks a lot for any help, I'd VERY appreciate explanations so I can learn.
You can pass buttonClick method in login button component like this
<LoginButton onClick={buttonClick} />
and inside Loginbutton component you can do something like this
<button type="button" ... onClick={props.onClick}>Login</button>
so when you click login button it will trigger you buttonClick method.
the LoginButton should get props and return a callback to the Main that will switch the state and you should also use useState to change the state in order to change the DOM,
thats how it should look
function Main() {
const [state, setState] = useState(false);
function buttonClick(event) {
setState((prev) => !prev);
}
return (
<div id='main'>
<p id='main-body'>{lorem}</p>
{ state ? <LoginForm /> : <LoginButton callback={buttonClick} />}
</div>
)
function LoginButton(props) {
const {callback} = props;
return (
<div class="btn-centered">
<button type="button" class="btn btn-outline-primary btn-lg btn-centered" onClick={callback}>Login</button>
</div>
)
}
I cannot for the life of me figure out why this isn't working. I have a WorkoutCard component:
WorkoutCard:
const key = require('weak-key');
function WorkoutCard({ workout }) {
const { userID } = useContext(AuthContext);
const [modalOpen, setModalOpen] = useState(false);
const closeModalCallback = () => setModalOpen(false);
return (
<div className="WorkoutCard" onClick={() => setModalOpen(true)}>
<div className="header">
<h2>{workout.name}</h2>
<h3>Days</h3>
{workout.days.map((day) => {
return (
<p key={key({})}>{day}</p>
)
})}
<button className="deleteButton" onClick={handleDelete}>Delete</button>
</div>
<div className="line"></div>
<div className="exercises">
<h3>Exercises</h3>
{workout.exercises.map((exercise) => {
return (
<p key={key({})}>{exercise}</p>
)
})}
</div>
<EditWorkoutModal key={key({})} modalOpen={modalOpen} closeModalCallback={closeModalCallback} />
</div>
);
}
export default WorkoutCard;
And I have the EditWorkoutModal component:
Modal.setAppElement('#root');
function EditWorkoutModal({ modalOpen, workout, closeModalCallback }) {
const { userID } = useContext(AuthContext);
return (
<Modal isOpen={modalOpen} className="editModal">
<div className="rightHalf">
<p className='closeButton' onClick={() => closeModalCallback()}>+</p>
</div>
</Modal>
)
}
export default EditWorkoutModal
The problem here is that closeModalCallback is not changing the state whatsoever. It is called, but modalOpen is still set to true.
And, this is even more confusing, because I have this functionality working in another part of the app. I have a workouts page that has both WorkoutCard components, as well as a Modal, and it works this way. However, the closeModalCallback on the WorkoutCard components' modals will not work.
onClick events bubble up the DOM. For example, see the below snippet (see browser console for output):
const App = () => {
const parent = () => console.log("parent");
const child = () => console.log("child");
return <div onClick={parent}>
<div onClick={child}>Click me</div>
</div>
}
ReactDOM.createRoot(document.body).render(<App />);
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/18.0.0/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/18.0.0/umd/react-dom.production.min.js"></script>
The above logs the following in the console:
> child
> parent
When you click on your child element, the click event bubbles up the DOM, eventually reaching your div with the WorkoutCard class, which fires the onClick that sets your modal-open state to true. You can stop the event from bubbling by calling e.stopPropagation() on your close-modal button:
onClick={(e) => {
e.stopPropagation();
closeModalCallback();
}}
This way the event won't bubble up to your parent div and trigger the onClick which is changing your state.
I am having hard times with this one.
I created a modal (SetLevel) so the user can select a level and after that what I want is to just update my initial state which goes by the name of level . So I pass my prop in handleChange in the SetLevel component like this:
const Game = () => {
const [levelOpen,setlevelOpen]=useState(false);
const [level,setlevel]=useState(1);
const changedLevel = (newLevel)=>{
console.log('newLevel',newLevel);
setlevel(newLevel);
}
return (
<div>
<h1 className='title'>Find the icons </h1>
<div className='container'>
<button className='btn' onClick={() => setlevelOpen(true)}>
Select level
</button>
<SetLevel isOpen={levelOpen} handleChange={()=>changedLevel(level)}
onClose={()=>setlevelOpen(false)}/>
</div>
<ItemDrag newLevel={level}/>
</div>
);
};
SetLevel child component looks like this:
const SetLevel = ({isOpen,onClose,handleChange}) => {
if (isOpen === false) return null;
const close = (e) => {
e.preventDefault();
if (onClose) {
onClose();
}
};
const handleClick =(num,e)=>{
console.log(num);
handleChange(num)
close(e)
}
return (
<div className='modal-window'>
<div>
<div onClick={(e) => close(e)} className='modal-close'>
CLOSE
</div>
<h1>Select level!</h1>
<button className="btn" onClick={(e)=>handleClick(3,e)} >LEVEL3</button>
</div>
</div>
);
}
So here is how I do it if a user selects level 3 I pass that number to my handleClick function and this function should take care of that handleChange prop as you can see.
But whenever I do this my level is not updating how come?? The value i am getting back is always 1 , why is that? thanks.
You don't take the return value of the handleChange in the Game component.
Try it like this:
const Game = () => {
const [levelOpen,setlevelOpen]=useState(false);
const [level,setlevel]=useState(1);
const changedLevel = (newLevel)=>{
console.log('newLevel',newLevel);
setlevel(newLevel);
}
return (
<div>
<h1 className='title'>Find the icons </h1>
<div className='container'>
<button className='btn' onClick={() => setlevelOpen(true)}>
Select level
</button>
<SetLevel isOpen={levelOpen} handleChange={(newLevel)=>changedLevel(newLevel)}
onClose={()=>setlevelOpen(false)}/>
</div>
<ItemDrag newLevel={level}/>
</div>
);
};
I am trying to render a custom and dynamic modal on button clicks. For example, when a "Game" button is clicked, I would like a modal to render with specfics about the game and when a "Bank" button is clicked, I would like the modal to populate with specfics about a bank.
First, when I add an onClick function to a custom button component, the modal does not render. However, when I put the onClick function on a regular button, the modal does render. How can I simply add an onClick function on any component to render a dynamic modal?
Second, I would like to populate each modal with differnet data. For example, a "Game" button would populate the modal with a title of "Game" and so on. I'm using props to do this, but is that the best solution?
Here is the code I have so far, but it is broken when I add the onClick function to components.
// Navbar.js
import { ModalContext } from '../contexts/ModalContext'
function Navbar() {
const [showModal, updateShowModal] = React.useState(false)
const toggleModal = () => updateShowModal((state) => !state)
return(
<ModalContext.Provider value={{ showModal, toggleModal }}>
<Modal
title="Title"
canShow={showModal}
updateModalState={toggleModal}
/>
</ModalContext.Provider>
)
// does not render a modal
<Button
onClick={toggleModal}
type="navItem"
label="Game"
icon="windows"
/>
// render a modal
<button onClick={toggleModal}>Show Modal</button>
)
}
import { ModalContext } from '../contexts/ModalContext'
// Modal.js
const Modal = ({ title }) => {
return (
<ModalContext.Consumer>
{(context) => {
if (context.showModal) {
return (
<div style={modalStyles}>
<h1>{title}</h1>
<button onClick={context.toggleModal}>X</button>
</div>
)
}
return null
}}
</ModalContext.Consumer>
)
}
// modalContext.js
export const ModalContext = React.createContext()
// Button.js
function Button({ label, type = 'default', icon }) {
return (
<ButtonStyle buttonType={type}>
{setIcon(icon)}
{label}
</ButtonStyle>
)
}
First problem:
I think the onClick prop of the <Button> component is not pointing to the onClick of the actual HTML button inside the component.
Could you please check that? And if you think It's been set up in the right way, then can you share the code of the component?
Second Problem
Yes, there's another way to do that. And I think it's React Composition. You can build the modal as the following:
<Modal
showModal={showModal}
updateModalState={toggleModal}
>
<div className="modal__header">{title}</div>
<div className="modal__body">{body}</div>
<div className="modal__footer">{footer}</div>
</Modal>
I think this pattern will give you more control over that component.
Issue
You are not passing the onClick prop through to the styled button component.
Solution
Given style-component button:
const ButtonStyle = styled.button``;
The custom Button component needs to pass all button props on to the ButtonStyle component.
// Button.js
function Button({ label, type='default', icon, onClick }) {
return (
<ButtonStyle buttonType={type} onClick={onClick}>
{setIcon(icon)}
{label}
</ButtonStyle>
)
}
If there are other button props then you can use the Spread syntax to collect them into a single object that can then be spread into the ButtonStyle component.
// Button.js
function Button({ label, type = 'default', icon, ...props }) {
return (
<ButtonStyle buttonType={type} {...props}>
{setIcon(icon)}
{label}
</ButtonStyle>
)
}
Second Question
For the second issue I suggest encapsulating the open/close/title state entirely in the modal context provider, along with the Modal component.
Here's an example implementation:
const ModalContext = React.createContext({
openModal: () => {},
});
const Modal = ({ title, onClose}) => (
<>
<h1>{title}</h1>
<button onClick={onClose}>X</button>
</>
)
const ModalProvider = ({ children }) => {
const [showModal, setShowModal] = React.useState(false);
const [title, setTitle] = React.useState('');
const openModal = (title) => {
setShowModal(true);
setTitle(title);
}
const closeModal = () => setShowModal(false);
return (
<ModalContext.Provider value={{ openModal }}>
{children}
{showModal && <Modal title={title} onClose={closeModal} />}
</ModalContext.Provider>
)
}
Example consumer to set/open a modal:
const OpenModalButton = ({ children }) => {
const { openModal } = useContext(ModalContext);
return <button onClick={() => openModal(children)}>{children}</button>
}
Example usage:
function App() {
return (
<ModalProvider>
<div className="App">
<h1>Hello CodeSandbox</h1>
<h2>Start editing to see some magic happen!</h2>
<OpenModalButton>Modal A</OpenModalButton>
<OpenModalButton>Modal B</OpenModalButton>
</div>
</ModalProvider>
);
}
Demo
I am trying to use useContext to create a generic Tooltip component that passes a close() function to the content inside the Tooltip. I have written my Tooltip like this
export function Tooltip(props) {
const [active, setActive] = useState(false);
const close = () => {
setActive(false);
}
return (
<div className="tooltip-wrapper"
onClick={() => setActive(true)}
>
{props.children}
<TooltipContext.Provider value={{close}}>
{active && (
<div className='tooltip-tip bottom' ref={node}>
{props.content}
</div>
)}
</TooltipContext.Provider>
</div>
)
}
I create the Tooltip in a different class component as follows
function Category(props) {
return (
<Tooltip content={<AddCategoryInnerTooltip name={props.name}/>}>
<p className="tooltip-name-opener">{props.name}</p>
</Tooltip>
);
}
function AddCategoryInnerTooltip(props) {
const {close} = useContext(TooltipContext);
return(
<div className="inner-tooltip-wrapper">
<input
className="tooltip-custom-input"
type="text"
defaultValue={props.name}
/>
<div className="button-end">
<button onClick={close}>Cancel</button>
<button>Ok</button>
</div>
</div>
)
}
When I attempt to call close within the AddCategoryInnerTooltip, the state passed from the Tooltip component doesn't update. When I console.log the state, it always comes as true without changing. What am I doing wrong?
should be a callback function
<button onClick={()=>close}>Cancel</button>