I have a modal that pops up on a dashboard if a condition is true and renders a checkbox. I can't seem to toggle to Modal off on the onClick function. Here is an example of the code.
Dashboard
const conditionalAgreement = false;
<Modal showModal={showModal} conditionalAgreement={conditionalAgreement} />
Modal
const Modal = ({ conditionalAgreement }) => {
const [showModal, setShowModal] = useState(false);
const [checkboxCondition, setCheckboxCondition = useState(false);
useEffect(() => {
if (conditionalAgreement) {
setShowModal(true);
}
}, [conditionalAgreement]);
const OnChangeHandler = () => {
setCheckboxCondition(!setCheckboxCondition);
};
const OnClickHandler = () => {
setShowModal(false);
};
return (
<div className={css.modal}>
<div className={css.checkbox}>
<CheckboxComponent
value={checkboxCondition}
onChange={OnChangeHandler}
description={tick this box"}
/>
</div>
<div className={css.buttonContainer}>
<ButtonComponent
onClick={OnClickHandler}
>
Save
</ButtonComponent>
</div>
</div>
);
};
export default Modal;
Dashboard:
const Dashboard = () => {
const [showModal, setShowModal] = useState(false);
return (
{showModal && (
<Modal showModal={showModal} closeModal={() => setShowModal(false)} />
)}
)
}
Modal:
const Modal = ({ showModal, closeModal }) => {
const [checkboxCondition, setCheckboxCondition] = useState(false);
const onChangeHandler = () => {
setCheckboxCondition(!checkboxCondition);
};
const onClickHandler = () => {
closeModal();
};
return (
<div className={css.modal}>
<div className={css.checkbox}>
<CheckboxComponent
value={checkboxCondition}
onChange={onChangeHandler}
description={tick this box"}
/>
</div>
<div className={css.buttonContainer}>
<ButtonComponent
onClick={onClickHandler}
>
Save
</ButtonComponent>
</div>
</div>
);
};
Now, as mention by #RobinZigmond something in your Dashboard component should set showModal to true so that your Modal appears.
Related
Please help me! Delete Icon is not functional, when I click on delete icon it delete all the contact, on refreshing, it returns all the previous contacts. I am also using localStorage.
I have added all the Component of the React App Project.
App.js
import { v4 as uuid } from "uuid";
const App = () => {
const LOCAL_STORAGE_KEY = "contacts";
const [contacts, setContacts] = useState([]);
const addContactHandler = (contact) => {
console.log(contact);
setContacts([...contacts, { id: uuid(), ...contact }]);
};
const removeContactHandler = (id) => {
const newContactList = contacts.filter((contact) => {
return contact.id !== id;
});
setContacts(newContactList);
};
useEffect(() => {
const retrieveContacts = JSON.parse(
localStorage.getItem(LOCAL_STORAGE_KEY)
);
if (retrieveContacts) {
setContacts(retrieveContacts);
}
}, []);
useEffect(() => {
if (contacts.length) {
localStorage.setItem(LOCAL_STORAGE_KEY, JSON.stringify(contacts));
}
}, [contacts]);
return (
<>
<div className="app">
<Header />
<AddContact addContactHandler={addContactHandler} />
<ContactList contacts={contacts} getContactId={removeContactHandler} />
</div>
</>
);
};
export default App;
ContactList.js
const ContactList = (props) => {
const deleteContactHandler = (id) => {
props.getContactId(id);
};
const renderContactList = props.contacts.map((contact) => {
return (
<>
<ContactCard
contact={contact}
clickHandler={deleteContactHandler}
key={contact.id}
/>
</>
);
});
return (
<>
<div className="contactList">
<h2 className="contactList__title">Contact List</h2>
<div className="contactList__container">
{renderContactList}
</div>
</div>
</>
);
};
ContactCard.js
const ContactCard = (props) => {
const { id, name, email } = props.contact;
return (
<>
<div className="contactCard">
<div className="contactCard__contact">
<img
className="contactCard__userIcon"
src={userIcon}
alt="user-icon"
/>
<div className="contactCard__userName">
<h2>{name}</h2>
<p>{email}</p>
</div>
</div>
<div className="contactCard__delIcon">
<img
src={delIcon}
alt="del-icon"
onClick={() => props.clickHandler(id)}
/>
</div>
</div>
</>
);
};
export default ContactCard;
I have researched out the references. Unable to get the Solution.
The effect to store the contacts do not save empty arrays.
Thats why you get the old array after refreshing your page.
Just remove the condition.
useEffect(() => {
localStorage.setItem(LOCAL_STORAGE_KEY, JSON.stringify(contacts));
}, [contacts]);
But you should consider to remove this effect.
Save the contacts directly after setting the state instead.
const addContactHandler = (contact) => {
console.log(contact);
const newContactList = [...contacts, { id: uuid(), ...contact }];
setContacts(newContactList);
localStorage.setItem(LOCAL_STORAGE_KEY, JSON.stringify(newContactList));
};
const removeContactHandler = (id) => {
const newContactList = contacts.filter((contact) => {
return contact.id !== id;
});
setContacts(newContactList);
localStorage.setItem(LOCAL_STORAGE_KEY, JSON.stringify(newContactList));
};
I would like to make something like this.
onClick --> post_query() starts --> spinner() shows --> post_query() ends --> modal shows
The problem I have is that spinner is showing before post_query() starts.
And I think it's because state loading is true( const [loading, setLoading] = useState(true); )
How can I make the spinner start after the post_query() start? (I tried using useEffect() but failed to handle with useEffect)
function Header() {
const [query, setQuery] = useState('');
const [isOpen, setIsOpen] = useState(false);
const [keys, setKeys] = useState([]);
const [loading, setLoading] = useState(true);
const modal_open = () => {
setIsOpen(true);
};
const modal_close = () => {
setIsOpen(false);
};
const post_query = (e) => {
var result = new Map()
axios.post('http://localhost:3000/api/' + query)
.then(function(response){
var content=JSON.parse(JSON.stringify(response)).data
for (var i=0;i<content.data.length;i++){
result.set(content.data[i].image_id, content.data[i].caption)
}
var key = Array.from(result.keys());
setKeys(key);
}).catch(err => {
console.log("error");
alert(err);
})
.finally(() => setLoading(false));
};
return (
<div className="header">
<Link to="/">
<img
src="logo.png"
className="header__logo"
/>
</Link>
<div className="header__search">
<input className="header__searchInput" type="search"
onChange={
(e) => {
setQuery(e.target.value);
}
}/>
<SearchIcon className="header__searchIcon"
onClick={(e)=> { post_query(); modal_open();}}/>
{loading && <div><CircularProgress className="spinner"/></div>}
{!loading &&
<Modal
open={isOpen}
onClose={modal_close} >
<Fade in={isOpen}>
<div className='modal_frame'>
<img src={'img'+keys[0]+'.jpg'} className='modal_img' />
</div>
</Fade>
</Modal>
}
</div>
</div>
);
}
export default Header;
Just add setLoading to true when call post_query and change initail value to false
const [loading, setLoading] = useState(false);
const post_query = (e) => {
var result = new Map()
setLoading(true)
axios.post('http://localhost:3000/api/' + query)
...
};
I am having a hard time rendering components conditionally in React. I have successfully rendered 2 components (A and B) conditionally but couldn't find any successful way to add a third component (C) in our case
this is the code for 2 componnets:
function App() {
const [click, setClick] = useState(true);
const ShowA = () => setClick(true);
const ShowB = () => setClick(false);
return (
<>
<br />
<button onClick={ShowA}>A </button>
<button onClick={ShowB}>B </button>
<div className="App">
{click && <div> A </div>}
{!click && <div>B</div>}
</div>
</>
);
}
Is there any possible way I can add a third C component so I can toggle between them? I have been trying for 2 days but no success.
This is the link of Codesandbox if anyone's interested
https://codesandbox.io/s/musing-tesla-9gkpw?file=/src/index.js:100-481
You can put as many states as you want:
function App() {
const [displayA, setDisplayA] = useState(true);
const [displayB, setDisplayB] = useState(true);
const [displayC, setDisplayC] = useState(true);
const showA = () => {
setDisplayA(true);
setDisplayB(false);
setDisplayC(false);
}
const showB = () => {
setDisplayA(false);
setDisplayB(true);
setDisplayC(false);
};
const showC = () => {
setDisplayA(false);
setDisplayB(false);
setDisplayC(true);
};
return (
<>
<br />
<button onClick={showA}>A</button>
<button onClick={showB}>B</button>
<button onClick={showC}>C</button>
<div className="App">
{displayA && <div>A</div>}
{displayB && <div>B</div>}
{displayC && <div>C</div>}
</div>
</>
);
}
And you can even put other things in your state, like JSX elements:
function App() {
const [elementToDisplay, setElementToDisplay] = useState("");
const showA = () => {
setElementToDisplay(<div>A</div>)
}
const showB = () => {
setElementToDisplay(<div>B</div>)
}
const showC = () => {
setElementToDisplay(<div>C</div>)
}
return (
<>
<br />
<button onClick={showA}>A</button>
<button onClick={showB}>B</button>
<button onClick={showC}>C</button>
<div className="App">
{elementToDisplay}
</div>
</>
);
}
You can save a state for the current button, and then show the different button conditionally using object lookup:
Check https://codesandbox.io/s/kind-haslett-b0fv0
function App() {
const [currentButton, setCurrentButton] = useState('A');
return (
<>
<br />
<button onClick={() => setCurrentButton('A')}>A</button>
<button onClick={() => setCurrentButton('B')}>B</button>
<button onClick={() => setCurrentButton('C')}>C</button>
<div className="App">
{
({
A: <div>A</div>,
B: <div>B</div>,
C: <div>C</div>
})[currentButton]
}
</div>
</>
);
}
tell me how when you click outside the window to close it? put a click on the main div, but it closes when I click even on the form input field. How to implement this idea?
export default () => {
const cn = useClassName('home-page');
const slider = useRef();
const onNext = () => slider.current.next();
const [active, setActive] = useState(false);
const onStart = () => {
setActive(true);
};
const closeModal = () => {
if (active) {
setActive(!active);
}
};
return (
<div className={cn()} onClick={closeModal}>
<Carousel dotsClass="test-className" ref={slider} effect="fade">
{dataSet.map(elem =>
<Slider title={elem.title} img={elem.image} onNext={onNext} onStart={onStart} key={elem.index}/>
)}
</Carousel>
<LoginForm style={active ? {display: 'block'} : {display: 'none'}}/>
</div>
);
};
Just started to learn about Reack hooks but I cannot figure out if it is possible to write a simple hook (or should I use some other approach, e.g. useEffect along with useState) in order to control visibility of multiple elements by clicking on different buttons on page.
Let's say I have a simple app with 2 buttons and 2 "modal" windows:
const App = () => {
const [firstModalOpen, toggleFirstModal] = useState(false);
const [secondModalOpen, toggleSecondModal] = useState(false);
return (
<div>
<button onClick={() => toggleFirstModal(true)}>Open First Modal</button>
<button onClick={() => toggleSecondModal(true)}>Open Second Modal</button>
<FirstModal
{...props}
show={firstModalOpen}
toggleModal={toggleFirstModal}
/>
<SecondModal
{...props}
show={secondModalOpen}
toggleModal={toggleSecondModal}
/>
</div>
)
}
const FirstModal = (props) => {
const { toggleModal, ...rest } = props;
return (
<Modal
{ ...rest }
show={firstModalOpen}
onHide={() => props.toggleModal(false)}
>
First modal content...
</Modal>
)
}
const SecondModal = (props) => {
const { toggleModal, ...rest } = props;
return (
<Modal
{ ...rest }
show={secondModalOpen}
onHide={() => props.toggleModal(false)}
>
Second modal content...
</Modal>
)
}
// state hook attempt
const useToggleModal = () => (init) => {
const [show, setToggleModal] = useState(init);
const toggleModal = () => setToggleModal(!show);
return { show, toggleModal };
};
Since those are react-bootstrap modal windows, they use show and onHide properties to determine/handle visibility and I have to pass rest prop to avoid some side-effects.
If I'd use my hook attempt in my app, I'd handle both modals on any button click so I came up with the idea to pass a string (to both, buttons and modals) which would tell which modal exactly to handle, but that approach for some reason looked a bit wrong.
Is there a "smarter" way in React to handle this internally instead of passing strings around?
If you have multiple modals and only one of them needs to open at once, then you must use a single state which stores which modal is opened, kind of like a string having the id of the modal. However if you want to open multiple modals, you would store the isOpen prop differently
For the first case you would write your code like
const App = () => {
const [openModal, toggleModal] = useState('');
return (
<div>
<button onClick={() => toggleModal('first')}>Open First Modal</button>
<button onClick={() => toggleModal('second')}>Open Second Modal</button>
<FirstModal
{...props}
show={openModal === 'first'}
toggleModal={toggleModal}
/>
<SecondModal
{...props}
show={secondModalOpen}
toggleModal={toggleModal}
/>
</div>
)
}
const FirstModal = (props) => {
const { toggleModal, ...rest } = props;
return (
<Modal
{ ...rest }
show={firstModalOpen}
onHide={() => props.toggleModal('first')}
>
First modal content...
</Modal>
)
}
const SecondModal = (props) => {
const { toggleModal, ...rest } = props;
return (
<Modal
{ ...rest }
show={secondModalOpen}
onHide={() => props.toggleModal('second')}
>
Second modal content...
</Modal>
)
}
For the second case it would be as you have written in your example, the only optimisation you can do for the second case is to store an array of modal objects and render them dynamically or let each modal handle its own toggle states and use useImperativeHandle to provide methods which parent can call to child modals like
const App = () => {
const firstRef = useRef(null);
const secondRef = useRef(null);
return (
<div>
<button onClick={() => this.firstRef.current.toggleModal()}>Open First Modal</button>
<button onClick={() => this.secondRef.current.toggleModal()}>Open Second Modal</button>
<FirstModal
{...props}
ref={firstRef}
/>
<SecondModal
{...props}
ref={secondRef}
/>
</div>
)
}
const FirstModal = forwardRef((props, ref) => {
const { showModal, toggleModal } = useToggleModal(false, ref);
return (
<Modal
{ ...rest }
show={showModal}
onHide={toggleModal}
>
First modal content...
</Modal>
)
})
const SecondModal = forwardRef((props, ref) => {
const { showModal, toggleModal } = useToggleModal(false, ref);
return (
<Modal
{ ...props }
show={showModal}
onHide={toggleModal}
>
Second modal content...
</Modal>
)
})
// state hook attempt
const useToggleModal = (init, ref) => {
const [show, setToggleModal] = useState(init);
const toggleModal = () => setToggleModal(!show);
useImperativeHandle(ref, () => ({
toggleModal
}))
return { show, toggleModal };
};