Remove a class clicking a button in reactjs - reactjs

I set class active when i click on a div.
const setActive = () => {
setActiveColor("active");
};
I want to delete this class when i click on click here to deselect color button from App component. How to do this?
Demo: https://codesandbox.io/s/exciting-elbakyan-hi12l?file=/src/App.js:541-569

const setActive = (flag) => {
setActiveColor(flag ? "active": "");
};
And you can set active as:
setActive(true);
or inactive as:
setActive(false);

You can have a state variable in your class and based on your clicks you can change that variable to add or remove class
class Demo{
constructor(){
this.state={addClass:true}
}
render(){
return({
this.state.addClass?<div className="some-class">With class</div>
:<div>Without class</div>
})
}
}
// Or
class Demo{
constructor(){
this.state={addClass:true}
}
render(){
return(
<div className={this.state.addClass?"some-name":null}>Lorem</div>
)
}
}

One approach could be to move activeStateColor to the parent.
Then when you click on the setActive you pass the key as reference.
export const Shape = ({ text, k, active, setActiveColor }) => {
const [state, setState] = useState({ visible: false });
const showModal = k => {
setState({
visible: true
});
setActiveColor(k);
};
const handleOk = e => {
console.log(e);
setState({
visible: false
});
};
const handleCancel = e => {
setActiveColor("");
console.log(e);
setState({
visible: false
});
};
const setActive = k => {
setActiveColor(k);
};
return (
<div>
<div>
<Button type="primary" onClick={() => showModal(k)}>
Open Modal
</Button>
<Modal
title="Basic Modal"
visible={state.visible}
onOk={handleOk}
onCancel={handleCancel}
>
<p>Some contents...</p>
<p>Some contents...</p>
<p>Some contents...</p>
</Modal>
</div>
<div>
<div
k={k}
className={active === k ? "active" : ""}
style={{ border: "1px solid" }}
onClick={() => setActive(k)}
>
{text}
</div>
</div>
</div>
);
};

Related

Hide modal on click outside in react hooks

i have a modal component in my react app and i need to close it on click outside
import React from "react";
import ReactDOM from "react-dom";
import style from "./Modal.module.scss";
const Modal = ({ isShowing, hide, childrenContent, childrenHeader }) =>
isShowing
? ReactDOM.createPortal(
<React.Fragment>
<div className={style.modalOverlay} />
<div
className={style.modalWrapper}
aria-modal
aria-hidden
tabIndex={-1}
role="dialog"
>
<div className={style.modal}>
<div className={style.modalHeader}>
{childrenHeader}
<button
type="button"
className={style.modalCloseButton}
data-dismiss="modal"
aria-label="Close"
onClick={hide}
>
<span aria-hidden="true">×</span>
</button>
</div>
{childrenContent}
</div>
</div>
</React.Fragment>,
document.body
)
: null;
export default Modal;
i was try to use this solution but it's not work in my code, how can i fix it?
Just a tip, when looking at the html you can use the native <dialog> tag, this is the semantically correct way to display a dialog type pop-up box, which yours looks to be.
https://developer.mozilla.org/en-US/docs/Web/HTML/Element/dialog
Dialog has a showModal() method, and a .close() method. This would be a better way of displaying a pop-up type dialog, than using <div> tags. It also allows you to use the native HTML5 methods, rather than trying to provide a work around using React.
I would reccomend this method over trying to look for work arounds
const Modal = ({ children, showModal, toggleModal }) => {
const wrapperRef = React.useRef(null);
const closeModal = React.useCallback(
({ target }) => {
if (
wrapperRef &&
wrapperRef.current &&
!wrapperRef.current.contains(target)
) {
toggleModal();
}
},
[toggleModal]
);
React.useEffect(() => {
document.addEventListener("click", closeModal, { capture: true });
return () => {
document.removeEventListener("click", closeModal, { capture: true });
};
}, [closeModal]);
return showModal
? ReactDOM.createPortal(
<>
<div ref={wrapperRef} className="modal">
{children}
</div>
</>,
document.body
)
: null;
};
Modal.propTypes = {
children: PropTypes.node.isRequired,
showModal: PropTypes.bool.isRequired,
toggleModal: PropTypes.func.isRequired
};
export default Modal;
in your parent component :
const Parent = () => {
const [showModal, setModalState] = React.useState(false);
const toggleModal = React.useCallback(() => {
setModalState((prevState) => !prevState);
}, []);
return (
<div>
<Modal showModal={showModal} toggleModal={toggleModal}>
<h1>Hello!</h1>
... some other childrens
<button
onClick={toggleModal}
>
Close
</button>
</Modal>
</div>
);
};

How to call a function with an argument after the submitting button in the popup window was clicked?

In React I created a component that holds a local state of popup. With a little help from onClick handler I change the local state to make the popup show up. The Popup component in turn contains confirm button. I would like to call a function deleteItem ONLY after the confirm button is clicked. But I don't get how to do it. In the code below the item gets deleted right after the popup shows up but it has to be deleted only if I press the button in the Popup component. If I understand correctely, the state of the components changes when the popup shows up and I have to get it khow to the MainComponent and only in this case the function deleteItem will be called.
import {deleteItem} from './item-reducer';
const MainComponent = ({items}) => {
const [visiblePopup, setVisiblePopup] = useState(false);
return(
{items.map(item => <li key={item.id}></li>
<img onClick={() => {
setVisiblePopup(true);
deleteList(item.id) // I have to call this function after the button
in the Popup component is pressed
}}
/>
)}
<Popup setVisiblePopup={setVisiblePopup}
)
}
Popup.jsx
<div onClick={() => setVisiblePopup(false)} />
Confirm
</div>
What I have to accomplish 1) I click img and popup shows up 2) I press
'Confirm' 3) function deleteItem is invoked 4)popup dissapeares.
If I understand you correctly this is what your looking for ?
const MainComponent = ({items}) => {
const [modalState, setModalState] = useState({
display: false,
deleteItemId: undefined
});
const modalCallback = useCallback((deleteItemId)=>{
deleteItem(deleteItemId)
setModalState({ display: false, deleteItemId: undefined })
},[])
return(
<Fragment>
{
items.map(item => (
<Fragment>
<li key={item.id}></li>
<img onClick={() => setModalState({ display: true, deleteItemId: item.id })} />
</Fragment>
))
}
<PopupModal
visible={modalState.display}
deleteItemId={modalState.deleteItemId}
callback={modalCallback}
/>
</Fragment>
)
}
const PopupModal = ({ visible, deleteItemId, callback }) => {
return (visible ? <div onClick={ () => callback(deleteItemId)}>Confirm</div> : null)
}
--- OR ----
const MainComponent = ({items}) => {
const [modalState, setModalState] = useState({
display: false,
deleteItemId: undefined
});
return(
<Fragment>
{
items.map(item => (
<Fragment>
<li key={item.id}></li>
<img onClick={() => setModalState({ display: true, deleteItemId: item.id })} />
</Fragment>
))
}
modalState.display ? <div onClick={() => [deleteList(modalState.deleteItemId), setModalState({display: false, deleteItemId: undefined}) ]}>Confirm</div> : null
</Fragment>
)
}

React-images gallery

I am trying to build an image gallery with React-images(https://github.com/jossmac/react-images).
Here is my code so far.
https://codesandbox.io/s/gallant-yalow-7srs6
Here I am trying to achieve two things:
Implement the modal and the modal will open with current select image from the base gallery.
Change the "of" inside active view of total view on footer. i.e. currently its "1 of 4" so I need this like "1 / 4"
Could anyone please help me? I am kind of lost :(
Thanks in advance.
So I was able to achieve your requirement,
Working example : https://codesandbox.io/s/xenodochial-dawn-scjsv
And here is the code:
class gall extends React.Component {
state = { modalIsOpen: false, currentIndex: 0 };
toggleModal = () => {
this.setState(state => ({ modalIsOpen: !state.modalIsOpen }));
};
onImageChange = (index) => {
console.log(index)
this.setState(state => ({ currentIndex: index }));
};
render() {
const { modalIsOpen } = this.state;
const CustomModalFooter = ({ currentIndex, views }) => {
const activeView = currentIndex + 1;
const totalViews = views.length;
if (!activeView || !totalViews) return null;
return (
<span class="react-images__footer__count css-w6xjhe css-1ycyyax">
{activeView} / {totalViews}
</span>
);
};
return (
<>
<button
type="button"
className="btn-fullScreen"
onClick={this.toggleModal}
>
Open Modal
</button>
<ModalGateway>
{modalIsOpen ? (
<Modal onClose={this.toggleModal}>
<Carousel
currentIndex={this.state.currentIndex}
components={{ FooterCount: CustomModalFooter }}
views={images}
/>
</Modal>
) : null}
</ModalGateway>
<Carousel
onClick={this.onImageClick}
trackProps={{onViewChange:(index) => this.onImageChange(index)}}
components={{ FooterCount: CustomModalFooter }}
views={images}
/>
</>
);
}
}
export default gall;

I change the renderer with one click and then directly query an item (ref). SetTimeout a good solution?

I change the renderer with one click and then directly query an item (ref). setTimeout a good solution?
(I don't know if I change the renderer in a single click, then in the event I can do anything in the fresh renderer. setTimeout good solution? Someone else has a different solution because I feel like I didn't do it well.)
import React from 'react';
class Test extends React.Component {
constructor(props) {
super(props);
this.state = {
con1: false,
con2: false
};
}
handleClick = (e) => {
parseFloat(e.currentTarget.getAttribute('data-id')) === 1 ?
(this.setState({
con1: true,
con2: false
}))
:
(this.setState({
con2: true,
con1: false
}));
/* Good, but this is valid??? */
setTimeout(()=>{
console.log(this.buttonContainer.childNodes[0])
},0)
/* Not good
console.log(this.buttonContainer.childNodes[0]);
*/
}
render() {
const { con1, con2 } = this.state;
return (
<div className="app-container">
<button
data-id="1"
onClick = {(e) => this.handleClick(e)}
>
Button
</button>
<button
data-id="2"
onClick = {(e) => this.handleClick(e)}
>
Button
</button>
<div
className="button-conteiner"
ref={(ref) => this.buttonContainer = ref}
>
{ con1 ?
(<div className="container1">container1</div>)
:
(null)
}
{ con2 ?
(<div className="container2">container2</div>)
:
(null)
}
</div>
</div>
)}
}
export default Test;
I would suggest to use useEffect hook with function components in this case. the logic will be more readable
import React, { useState, useEffect, useRef } from "react";
const Test = props => {
const [con1, setCon1] = useState(false);
const [con2, setCon2] = useState(false);
const buttonContainer = useRef(null);
const handleClick = e => {
const b = parseFloat(e.currentTarget.getAttribute("data-id")) === 1;
setCon1(b);
setCon2(!b);
};
useEffect(() => {
if(buttonContainer.current) {
console.log(buttonContainer.current.childNodes[0])
}
}, [buttonContainer.current, con1, con2])
return (
<div className="app-container">
<button data-id="1" onClick={e => this.handleClick(e)}>
Button
</button>
<button data-id="2" onClick={e => this.handleClick(e)}>
Button
</button>
<div
className="button-conteiner"
ref={buttonContainer}
>
{con1 ? <div className="container1">container1</div> : null}
{con2 ? <div className="container2">container2</div> : null}
</div>
</div>
);
};
export default Test;

Passing state between nested components

I am trying to open up a Modal component on a onClick from a listItem in a listGroup component. However, the setup I currently have either causes my application to hang and I am not able to click anything on the application or the state does not get updated and the modal does not render.
Another weird that thing that occurs is when I console log to see what the showModalState is, the state changes but when I check the react developer tools to see if it changed, it's always at the initial state which is false.
The error more than likely comes from the ModalActions.ts or ModalReducer.ts.
Note: All the code provided below are just snippets. I omitted alot of stuff and left only what I thought could be the issue.
This is my ModalTypes.ts
export const SHOW_MODAL = "SHOW_MODAL";
interface ShowModal {
type: typeof SHOW_MODAL;
payload: boolean;
}
export type ModalActionTypes = ShowModal;
This is my ModalActions.ts
import { SHOW_MODAL, ModalActionTypes } from "./ModalTypes";
export function UpdateModal(modal: boolean): ModalActionTypes {
return {
type: SHOW_MODAL,
payload: modal
};
}
This is my IModalState.ts
export interface IModalState {
showModal: boolean;
}
This is my ModalReducer.ts. **I will probably make actions and types to hide the modal as well
import { ModalActionTypes, SHOW_MODAL } from "../actions/ModalTypes";
import { IModalState } from "../models/IModalState";
const initialState: IModalState = {
showModal: false
};
export function modalReducer(state = initialState, action: ModalActionTypes) {
switch (action.type) {
case SHOW_MODAL:
return {
...state,
showModal: action.payload
};
default:
return state;
}
}
This is my App.tsx
<ListGroup
onUpdateModal={this.props.onUpdateModal}
showModalState={this.props.showModalState}
/>
const mapStateToProps = (state: AppState) => ({
showModalState: state.modal
});
const mapDispatchToProps = (dispatch: any) => {
return {
onUpdateModal: bindActionCreators(UpdateModal, dispatch)
};
};
export default connect(
mapStateToProps,
mapDispatchToProps
)(App);
This is my ListGroup.tsx
import { UpdateModal } from "../actions/ModalActions";
import { IModalState } from "../models/IModalState";
interface IProps {
onUpdateModal: typeof UpdateModal;
showModalState: IModalState;
}
// interface IState {
// showModal: boolean;
// }
export class ListGroup extends React.Component<IProps> {
// IState
// public state: IState = {
// showModal: false
// };
// showModal = () => {
// // Show the modal
// this.setState({ showModal: true });
// };
public render() {
// const { showModal } = this.state;
return (
<div>
<ul
className="list-group"
style={{
marginTop: "20px",
display: "inline-block"
}}
>
{filterTests.map(filterTest => (
<li
className="list-group-item list-group-item-action d-flex justify-content-between align-items-center"
onClick={() => {
this.props.onUpdateModal(true);
console.log(this.props.onUpdateModal(true));
console.log(this.props.showModalState);
this.props.onUpdateSelectedTest(filterTest);
// this.showModal();
}}
>
{filterTest.companyPN}: {filterTest.description}
</li>
))}
</ul>
{/* Show the modal if showModal is true */}
{this.props.showModalState && (
<TestGroup
testState={this.props.testState}
onUpdateSelectedWedge={this.props.onUpdateSelectedWedge}
/>
)}
</div>
);
}
}
This my TestGroup.tsx
interface IProps {
onUpdateModal: typeof UpdateModal;
showModalState: IModalState;
}
export class TestGroup extends React.Component<IProps> {
// hideModal = () => {
// this.setState({
// showModal: !this.props.showModal
// });
// };
public render() {
return (
<div>
<div className="modal" style={{ display: "inline-block" }}>
<div className="modal-dialog" role="document">
<div className="modal-content">
<div className="modal-header">
<h5 className="modal-title"></h5>
<button
type="button"
className="close"
data-dismiss="modal"
aria-label="Close"
>
<span aria-hidden="true">×</span>
</button>
</div>
<div className="modal-body">
</div>
<div className="modal-footer">
<button
// onClick={() => {
// this.hideModal();
// }}
type="button"
className="btn btn-secondary"
data-dismiss="modal"
>
Close
</button>
</div>
</div>
))}
</div>
</div>
</div>
);
}
}
export default TestGroup;
I'd rather leave this as a comment, but I don't have the privilege at the moment.
In your app.ts mapStateToProps function, I think you want showModalState to be
showModalState: state.modal.showModal
In Apps.tsx instead of this.props.showModalState it should have been this.props.showModalState.showModal
{this.props.showModalState.showModal && (
<TestGroup
testState={this.props.testState}
onUpdateSelectedWedge={this.props.onUpdateSelectedWedge}
/>
)}

Resources