React TypeScript Modal - reactjs

Modal.tsx
import React from 'react';
import './Modal.css'
interface IProps {
show?: boolean;
handleClose?: any;
children: any;
};
const Modal:React.SFC<IProps> = ({ handleClose, show, children }: IProps) => {
const showHideClassName = show ? "display-block" : "display-none";
return <div className={showHideClassName}>
<section className="modal-main">
{children}
<button onClick={handleClose}>close</button>
</section>
</div>;
};
export default Modal;
infoPresenter.tsx
import { Icon } from 'antd';
import React from 'react';
import Modal from '../Modal';
import './Info.css';
class Info extends React.Component{
public state = {
show: false
};
public showModal = () => {
this.setState({ show:true })
}
public hideModal = () => {
this.setState({ show:false })
}
public render(){
return (
<section className="Info">
<div className="InfoTitle">
<h1>Philociphy</h1>
<p>
lorem ipsum
</p>
</div>
<div className="WholeBox">
<div className="BoxLeft" onClick={this.showModal}>
<Modal show={this.state.show} handleClose={this.hideModal}>
<p>Modal</p>
<p>Data</p>
</Modal>
<p>VISION</p>
<Icon type="arrow-left" />
</div>
<div className="BoxRight">
<p>VALUE</p>
<Icon type="arrow-right" />
</div>
</div>
</section>
)
}
}
export default Info;
I want to make the modal. it works but not vanish.
I checked all my state and props, but couldn't find problems.
I don't know where's wrong in my code.
please find it and fix it.
if you have more good idea which make the modal, please let me know how make it in typescript react.
Thanks for all guys who watch this questions

Related

reactjs Modal not being called

Hi everyone I am new to react but have the task to implement this modal. Unfortunately If I click on the button nothing happens. I could not find a solution and to my total confusion it seems like there are many different ways to implement this kind of modals.
Does anyone have a suggestion please?
The modal itself:
import React, { useState } from 'react'
import './BookingUpdate.css';
function Modal({ setOpenModal, children } : {setOpenModal:any, children:any}) {
if(!setOpenModal) return null;
return (
<div className="modalBackground">
<div className="modalContainer">
<div className="titleCloseBtn">
<button
onClick={() => {
setOpenModal(false);
}}
>
X
</button>
</div>
<div className="title">
<h1>Are You Sure You Want to Continue?</h1>
</div>
<div className="body">
<p>The next page looks amazing. Hope you want to go there!</p>
</div>
<div className="footer">
<button
onClick={() => {
setOpenModal(false);
}}
id="cancelBtn"
>
Cancel
</button>
<button>Continue</button>
</div>
</div>
</div>
);
}
export default Modal;
The file from which it is called:
import React, { useState } from 'react';
import FullLayout from '../components/FullLayout';
import Loading from '../components/Loading';
import { Booking, Formatting } from 'flexspace-commons';
import { Table, Form, Col, Row, Button } from 'react-bootstrap';
import { Search as IconSearch, Download as IconDownload, X as IconX , Settings as IconSettings } from 'react-feather';
import ExcellentExport from 'excellentexport';
import { withTranslation } from 'react-i18next';
import { TFunction } from 'i18next';
import type * as CSS from 'csstype';
import Modal from "../components/BookingUpdate";
interface State {
loading: boolean
start: string
end: string
modalIsOpen: boolean
}
interface Props {
t: TFunction
}
class Bookings extends React.Component<Props, State> {
data: Booking[];
constructor(props: any) {
super(props);
this.data = [];
let end = new Date();
let start = new Date();
start.setDate(end.getDate() - 7);
this.state = {
loading: true,
start: Formatting.getISO8601(start),
end: Formatting.getISO8601(end),
modalIsOpen: false
};
this.toggleBookingModal = this.toggleBookingModal.bind( this );
}
.
.
.
toggleBookingModal = (booking: Booking) => {
this.setState( { modalIsOpen: ! this.state.modalIsOpen } );
<Modal setOpenModal={this.state.modalIsOpen} >
</Modal>
}
renderItem = (booking: Booking) => {
const btnStyle: CSS.Properties = {
['padding' as any]: '0.1rem 0.3rem',
['font-size' as any]: '0.875rem',
['border-radius' as any]: '0.2rem',
};
return (
<tr key={booking.id}>
<td>{booking.user.email}</td>
<td>{booking.space.location.name}</td>
<td>{booking.space.name}</td>
<td>{Formatting.getFormatterShort().format(booking.enter)}</td>
<td>{Formatting.getFormatterShort().format(booking.leave)}</td>
<td><Button variant="info" style={btnStyle} onClick={() => this.toggleBookingModal(booking)}><IconSettings className="feather" /></Button></td>
<td><Button variant="danger" style={btnStyle} onClick={() => this.cancelBooking(booking)}><IconX className="feather" /></Button></td>
</tr>
);
}
.
.
.

How can I use forwardRef in React Component?

Can someone help me? I'm creating a component inside React, and I want to make it more accessible using forwardRef. In my case, I'm making a button and I'm using the button's properties, and a few more I've done to make it more dynamic.
This is a summary of my code.
export interface ButtonProps
extends React.DetailedHTMLProps<React.ButtonHTMLAttributes<HTMLButtonElement>, HTMLButtonElement> {
children?: React.ReactNode;
loading?: boolean;
}
class Button extends React.Component<ButtonProps> {
render() {
const {
...otherProps
} = this.props;
return (
<button {...(otherProps)}></button>
)
}
}
export default Button;
I tried to start something, but right away it gave an error
const ForwardedElement = React.forwardRef<ButtonProps, HTMLButtonElement> (
(props: ButtonProps, ref) => <Button {...props}/>
)
export default ForwardedElement;
I suggest you to use useImperativeHandle hook
useImperativeHandle customizes the instance value that is exposed to parent components when using ref. Let's visualize that with an example.
Here's a component as search bar
import React, {forwardRef, useImperativeHandle, useRef} from "react";
import {Form} from "reactstrap";
const SearchBar = (props, ref) => {
const buttonRef = useRef<any>();
useImperativeHandle(ref, () => ({
getCurrentValue: () => {
return buttonRef.current ? buttonRef.current["value"] : '';
},
setCurrentValue: (value) => {
if (buttonRef.current) {
buttonRef.current["value"] = value;
}
}
}));
return (
<Form className="p-3 w-100" onSubmit={(e) => props.onSubmitHandler(e)}>
<div className="form-group m-0">
<div className="input-group">
<input
type="text"
className="form-control"
placeholder="Search ..."
aria-label="Word to be searched"
ref={buttonRef}
/>
<div className="input-group-append">
<button className="btn btn-primary" type="submit">
<i className="mdi mdi-magnify" />
</button>
</div>
</div>
</div>
</Form>
);
}
export default forwardRef(SearchBar);
This is the header component in which we call our search bar component
import React, {useEffect, useRef, useState} from 'react';
import SearchBar from '../Form/Search/SearchBar';
import Router from 'next/router';
const Header = () => {
const mobileSearchRef = useRef<any>();
const [search, setSearch] = useState<any>(false);
const codeSearchHandler = (e) => {
e.preventDefault();
setSearch(!search);
if (mobileSearchRef.current) {
if (mobileSearchRef.current.getCurrentValue() == '') {
return;
}
}
Router.push({
pathname: '/search',
query: {
searchTerm: mobileSearchRef.current
? mobileSearchRef.current.getCurrentValue()
: ''
},
});
mobileSearchRef.current.setCurrentValue('');
};
return (
<React.Fragment>
<header id="page-topbar">
<div className="navbar-header">
<div className="d-flex">
<div className="dropdown d-inline-block d-lg-none ms-2">
<button
onClick={() => {
setSearch(!search);
}}
type="button"
className="btn header-item noti-icon mt-2"
id="page-header-search-dropdown"
>
<i className="mdi mdi-magnify" />
</button>
<div
className={
search
? 'dropdown-menu dropdown-menu-lg dropdown-menu-end p-0 show'
: 'dropdown-menu dropdown-menu-lg dropdown-menu-end p-0'
}
aria-labelledby="page-header-search-dropdown"
>
<SearchBar
id="headerSearchBar"
ref={mobileSearchRef}
onSubmitHandler={(e) => codeSearchHandler(e)}
/>
</div>
</div>
</div>
</div>
</header>
</React.Fragment>
);
};
export default Header;
If we look at the header component, we can see that we get the input value of search bar component using mobileSearchRef and getCurrentValue method. We can also set its value using setCurrentValue method.
You have to pass the ref aside the spread props:
export interface ButtonProps
extends React.DetailedHTMLProps<React.ButtonHTMLAttributes<HTMLButtonElement>, HTMLButtonElement> {
children?: React.ReactNode;
loading?: boolean;
ref?: React.RefObject<HTMLButtonElement>
}
class Button extends React.Component<ButtonProps> {
render() {
const {
...otherProps,
ref
} = this.props;
return (
<button {...otherProps} ref={ref}></button>
)
}
}
export default Button;
const ForwardedElement = React.forwardRef<ButtonProps, HTMLButtonElement> (
(props: ButtonProps, ref) => <Button {...props} ref={ref}/>
)
export default ForwardedElement;
now it should work, see this question

Error: Type '{ children: Element; }' has no properties in common with type 'IntrinsicAttributes & Pick<ClassAttributes<Layout>&Props, "ref" | "key">'

Error: Type '{ children: Element; }' has no properties in common with
type 'IntrinsicAttributes & Pick<ClassAttributes & Props,
"ref" | "key">'.
I'm new learner of reactjs with typescript and I follow the https://www.youtube.com/watch?v=8jHKBAPNtpM tutorial for learning, but things are not explained properly on this video.
Can Anyone help me for resolve this issue.
My HomePage.tsx file
import React, { Component } from "react";
import Layout from "../../components/common/layout";
import Content from "../../components/common/content";
import Home from "./../../components/home";
class HomePage extends Component {
render() {
return (
<div className="wrapper">
<Layout>
<Content title="Dashboard">
<Home />
</Content>
</Layout>
</div>
);
}
}
export default HomePage;
my Layout.tsx file
import React, { Component } from "react";
import { connect } from "react-redux";
import TopNav from "../topnav";
import Aside from "../aside";
import UserStateInterface from "../../../interfaces/UserStateInterface";
import UserService from "../../../services/UserService";
import { setUser } from "./../../../store/actions";
interface Props {
user: UserStateInterface;
setUser: typeof setUser;
}
class Layout extends Component<Props> {
async componentDidMount() {
const response = await UserService.getCurrentUserProfile();
this.props.setUser(response);
}
render() {
return (
<React.Fragment>
<TopNav />
<Aside user={this.props.user} />
{this.props.children}
</React.Fragment>
);
}
}
const mapStateToProps = (state) => {
return {
user: state.user,
};
};
export default connect(mapStateToProps, { setUser })(Layout);
my Content.tsx file
import React, { Component } from "react";
interface Props {
title: String;
}
class Content extends Component<Props> {
render() {
const { title } = this.props;
return (
<div className="content-wrapper">
<section className="content-header">
<div className="container-fluid">
<div className="row mb-2">
<div className="col-sm-6">
<h1>{title}</h1>
</div>
<div className="col-sm-6">
<ol className="breadcrumb float-sm-right">
<li className="breadcrumb-item">
<a href="/" onClick={(event) => event.preventDefault()}>
Home
</a>
</li>
<li className="breadcrumb-item active">Blank Page</li>
</ol>
</div>
</div>
</div>
</section>
<section className="content">{this.props.children}</section>
</div>
);
}
}
export default Content;
my Home.tsx file
import React, { Component } from "react";
import Card from "./../common/card";
import TopCards from "./topcards";
import TodoWrapper from "../todo/todowrapper";
class Home extends Component {
render() {
return (
<React.Fragment>
<TopCards />
<div className="row">
<div className="col-md-6">
<TodoWrapper />
</div>
<div className="col-md-5">
<Card title="Some content will come" titleIcon="ion-clipboard">
<p>Content will come.</p>
</Card>
</div>
</div>
</React.Fragment>
);
}
}
export default Home;
You need to tell react that the component is ready to accept children.
React provides a utility for exactly this. Just replace your interface Props {...} with type Props = PropsWithChildren<{...}>.
import { PropsWithChildren } from "react";
type Props = PropsWithChildren<{
user: UserStateInterface;
setUser: typeof setUser;
}>;
Example: https://codesandbox.io/s/boring-chatelet-kp5vm?file=/src/Layout.tsx

How to set state properly in react js

I have 2 react components -
import React, {Component} from 'react'
import './App.css';
import ContactCard from './components/ContactCard'
class App extends Component {
render() {
return (
<div className="App-header">
<ContactCard name="hi 1" age={36} email="hu#yahoo.com"/>
<ContactCard name="hi 2" age={67} email="hi#yahoo.com"/>
<ContactCard name="hi 2" age={42} email="he#yahoo.com"/>
</div>
);
}
}
export default App;
import React, { Component } from "react";
class ContactCard extends Component {
state = {
showAge: false,
};
setAge = () => {
this.setState({
showAge: !this.state.showAge,
});
};
render() {
return (
<div className="contactCard">
<div className="userDetails">
<h2>Name: {this.props.name}</h2>
<p>Email: {this.props.email}</p>
<button onClick={this.state.setAge}>Show Age</button>
{this.state.showAge && <p>Age: {this.props.age}</p>}
</div>
</div>
);
}
}
export default ContactCard;
Toggle button is not working. i have set the state before render method. its not mandatory to set the state in the constructor.
Now the error is gone but still toggle button not working.
Whats going wrong?
You are setting state the wrong way. It should be:
setAge = () => {
this.setState({
showAge: !this.state.showAge
});
};
Edit
In order to make your app work as expected, you also have to set the click handler properly:
<button onClick={this.setAge}>Show Age</button>
You have to update your code its this.setAge not this.state.setAge
render() {
return (
<div className="contactCard">
<div className="userDetails">
<h2>Name: {this.props.name}</h2>
<p>Email: {this.props.email}</p>
<button onClick={this.setAge}>Show Age</button>
{this.state.showAge && <p>Age: {this.props.age}</p>}
</div>
</div>
);
}

Rendering a Bootstrap Component Using JSX

Editing for clarity: I cannot figure out how to dynamically create Boostrap Components using JSX in a react app. End goal is to get the new button in the "newBtnSpace" div when the first button is clicked. I have tried using show.hide methods, but those need to be hard coded. Trying to create buttons based off an array. code:
./components/newBSBtnSpaceFunc.js
import React, { Component } from 'react'
import { Button } from 'reactstrap'
export default function NewBSBtnFunc() {
let BtnArray = ["red", "blue", "green"].map((btn) => {
return React.createElement(
Button,
{variant: 'primary'},
'New Button',
{id: "newBtn"},
btn
)
}
./components/BSBtn.js
import React, { Component } from 'react'
import { Button } from 'reactstrap'
import NewBSBtnFunc from "./NewBSBtnFunc"
export default class BSBtn extends Component {
render() {
return (
<div>
<Button onClick={NewBSBtnFunc}>Click Me</Button>
<div id="newBtnSpace"></div>
</div>
)
}
}
App.js
import React from 'react';
import 'bootstrap/dist/css/bootstrap.min.css';
import BSBtn from "./components/BSBtn"
function App() {
return (
<div>
<BSBtn></BSBtn>
</div>
);
}
export default App;
github link: https://github.com/mollygilbert389/testingBootstrapBtn
You can conditionally show the new button by setting a state item (in this case showNewButton) to true in the onClick of the original button.
render() {
return (
<div>
<Button onClick={() => this.setState({ showNewButton: true }))}>Click Me</Button>
<div id="newBtnSpace">{ this.state.showNewButton && <Button variant="primary" id="newBtn">New Button</Button> }</div>
</div>
)
}
PS you've already successfully worked out how to create Bootstrap buttons in jsx:
<Button onClick={NewBSBtnFunc}>Click Me</Button>
onClick does not expect a return value so returning the new button won't do anything.
The way you have things organized makes it very difficult since you can't return anything from the function, and you can't modify state from outside the class. I would suggest moving your click handler into the component and using to to modify a state value that will show the second button.
Here is my suggestion:
import React, { Component } from 'react'
import { Button } from 'reactstrap'
export default class BSBtn extends Component {
state = {show: false}
handleClick = () => {
this.setState({ show: !this.state.show })
}
render() {
return (
<div>
<Button onClick={this.handleClick}>Click Me</Button>
<div id="newBtnSpace">
{this.state.show ?
<Button variant="primary" id="newBtn">New Button</Button>
: null}
</div>
</div>
)
}
}
Updated solution to your updated question:
class BSBtn extends React.Component {
state = {
show: false,
buttons: []
}
handleClick = () => {
this.setState({ show: !this.state.show })
}
handleAdd = () => {
this.setState({ buttons: [...this.state.buttons, (this.state.buttons.length + 1)] })
}
render() {
return (
<div>
<h3>Option 1</h3>
<button onClick={this.handleClick}>Click Me</button>
<div id="newBtnSpace">
{this.state.show ? [1,2,3].map((value) => (
<div>
<button>Button {value}</button>
</div>
))
: null}
</div>
<hr/>
<div style={{ marginTop: '30px' }}>
<h3>Option 2</h3>
<button onClick={this.handleAdd}>Click Me</button>
{this.state.buttons.map((value) => (
<div>
<button>Button {value}</button>
</div>
))}
</div>
</div>
)
}
}
ReactDOM.render(<BSBtn />, document.getElementById('root'))
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.6.3/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.6.3/umd/react-dom.production.min.js"></script>
<div id='root' />

Resources