I am starting with React. I am trying to send a var and function to my component. I know that it is a bad practice to use :any that is why I want to change for a proper way.
I am doing a modal and I am sending the data to my component this way. I am using useState
Datatable.tsx
import { useEffect, useMemo, useState } from "react";
import Modal from "../modal/Modal";
const Datatable = () => {
const [show, setShow] = useState<boolean>(false);
return (
<div>
<Modal show={show} closeModal={() => setShow(false)} />
<button onClick={() =>setShow((s) => !s)}>
Open Modal
</button>
<tableStuff/>
<div/>
);
Modal.tsx
import "./modal.scss";
import React from "react";
import ReactDOM from "react-dom";
const Modal = (props:any) => {
const portal = document.getElementById("portal");
if (!portal) {
return null;
}
if (!props.show) {
return null;
}
return ReactDOM.createPortal(
<>
<div className="modal" onClick={props.closeModal}>
<div className="content">
<h2>Simple modal</h2>
</div>
</div>
</>,
portal
);
};
export default Modal;
I have seen this on tons of videos, but the following piece of code does not work for me.
I am getting this error Binding element 'show' implicitly has an 'any' type and Binding element 'closeModal' implicitly has an 'any' type
//...
const Modal = ({show, closeModal}) => {
if (show) {
return null;
}
//...
return ReactDOM.createPortal(
<>
<div className="modals" onClick={closeModal}>
<button onClick={closeModal}>Close</button>
</div>
</>,
portal
);
}
Is something else I am missing in order to not use (props:any)? Any help or suggestion would be nice.
interface ModalProps {
show: boolean;
closeModal: () => void;
}
const Modal = ({show, closeModal}: ModalProps) => {
Related
I'm trying to implement the following toast manager to my react typescript application
https://codesandbox.io/s/react-toasts-melne?from-embed=&file=/src/contexts/ToastContext.js
I've created a file called toast-context.tsx
import React, { useCallback, useEffect, useState, createContext } from 'react';
const ToastContext = createContext(null);
export default ToastContext;
export function ToastContextProvider({ children }) {
const [toasts, setToasts] = useState([]);
useEffect(() => {
if (toasts.length > 0) {
const timer = setTimeout(() => setToasts((toasts) => toasts.slice(1)), 6000);
return () => clearTimeout(timer);
}
}, [toasts]);
const addToast = useCallback(
function (toast) {
setToasts((toasts) => [...toasts, toast]);
},
[setToasts],
);
return (
<ToastContext.Provider value={addToast}>
{children}
<div style={{ position: 'fixed', bottom: '1 rem', left: '1 rem' }}>
{toasts.map((toast) => (
<div style={{ background: 'green', color: 'white' }} key={toast}>
{toast}
</div>
))}
</div>
</ToastContext.Provider>
);
}
And a hook as useToast
import { useContext } from 'react';
import ToastContext from '../contexts/toast-context';
export default function useToast() {
return useContext(ToastContext);
}
And in my _app.tsx (This is nextjs)
return (
<>
<div className="app">
<ToastContextProvider>
<ThemeProvider theme={theme}>
<Component {...pageProps} />
</ThemeProvider>
</ToastContextProvider>
</div>
</>
);
};
But when i try to use the solution, it says Uncaught TypeError: addToast is not a function
export const ToastTest = () => {
const [text, setText] = useState('');
const addToast = useToast();
function handleTextChange(event) {
setText(event.target.value);
}
function handleClick() {
addToast(text);
}
return (
<div>
<h1>Hello Toasts!</h1>
<div>
<input value={text} onChange={handleTextChange} />
</div>
<button onClick={handleClick}>Show toast</button>
</div>
);
};
I followed everything that was in the example. In the example it works but not sure why it does not work in my code.Can someone please point out the issue?
When i click on the ''Show toast' button this is the error i'm getting
The error message says that it is a "TypeError" but actually this is not related to typescript. That is a JavaScript error which occurs if you try to invoke something that isn't actually a function. For example, null is not a function and would throw this error if you tried to invoke it.
The error is most likely that you are not rendering your ToastTest component inside of your ToastContextProvider component. Your call to useToast is probably getting the default value of "null" and throwing this error when it tries to call "null" as a function. Make sure that your ToastContextProvider is a parent or grandparent to your ToastTest component.
I am trying to open a modal from another component. this is in my parent component:
import { Button, Modal } from 'react-bootstrap';
import React, { useState } from 'react';
import './App.css';
import ModalB from './ModalB';
function App() {
const [showA, setShowA] = useState(false);
const [showB, setShowB] = useState(false);
const handleCloseA = () => setShowA(false);
const handleShowA = () => setShowA(true);
const handleShowB = ({ handleShow }) => {
setShowB(handleShow);
};
return (
<div className="App">
<header className="App-header">
<Button variant="primary" onClick={handleShowA}>Open A</Button>
<Button variant="primary" onClick={handleShowB}>Open B</Button>
<Modal show={showA} onHide={handleCloseA}>
<Modal.Header closeButton>
<Modal.Title>In Modal A</Modal.Title>
</Modal.Header>
</Modal>
<ModalB isModalVisible={showB}></ModalB>
</header>
</div>
);
}
export default App;
And Modal B component:
import { Button, Modal } from 'react-bootstrap';
import React, { useState } from 'react';
import { propTypes } from 'react-bootstrap/esm/Image';
const ModalB = (props) => {
const [showB, setShowB] = useState(false);
const handleCloseB = () => setShowB(false);
const handleShowB = () => setShowB(true);
return (
<div>
<Modal show={props.isModalVisible} onHide={handleCloseB}>
<Modal.Header closeButton>
<Modal.Title>In Modal B</Modal.Title>
</Modal.Header>
</Modal>
</div>
);
}
export default ModalB;
The problem is to display B from the main component. While displaying modalA is simple, I don't understand how to tell B to display from the main component.
Thanks for your help.
Remove the "show" state from ModalB and pass in the handleShowB handler from the parent.
const ModalB = ({ isModalVisible, handleShowB }) => {
return (
<div>
<Modal show={props.isModalVisible} onHide={handleShowB}>
<Modal.Header closeButton>
<Modal.Title>In Modal B</Modal.Title>
</Modal.Header>
</Modal>
</div>
);
}
In parent pass handleShowB handler. Here we just pass an anonymous callback to call the setShowB state updater and update the showB state to be false.
<ModalB
isModalVisible={showB}
handleShowB={() => setShowB(false)}
/>
const handleShowB = ({ handleShow }) => {
setShowB(handleShow); };
Why do you need handleShow if this is undefined?
Just update your state based on current state and the rest of your code would work fine.
const handleShowB = () => setShowB(!showB);
This will trigger the current modal state (weather hide/show) for ModalB component and it will render accordingly.
Here's working demo: https://codesandbox.io/s/show-multiple-modals-edv6y
I' m new to React and I'm building a simple React app that displays all the nations of the world on the screen and a small search bar that shows the data of the searched nation.
Here an image of the site
But I don't know how to show the country you want to click in the scrollbar.
Here the app.js code:
import React, { Component } from 'react';
import './App.css';
import NavBar from '../Components/NavBar';
import SideBar from './SideBar';
import CountryList from '../Components/SideBarComponents/CountryList';
import Scroll from '../Components/SideBarComponents/Scroll';
import Main from './Main';
import SearchCountry from '../Components/MainComponents/SearchCountry';
import SearchedCountry from '../Components/MainComponents/SearchedCountry';
import Datas from '../Components/MainComponents/Datas';
class App extends Component {
constructor() {
super();
this.state = {
nations: [],
searchField: '',
button: false
}
}
onSearchChange = (event) => {
this.setState({searchField: event.target.value});
console.log(this.state.searchField)
}
onClickChange = () => {
this.setState(prevsState => ({
button: true
}))
}
render() {
const {nations, searchField, button, searchMemory} = this.state;
const searchedNation = nations.filter(nation => {
if(button) {
return nation.name.toLowerCase().includes(searchField.toLowerCase())
}
});
return (
<div>
<div>
<NavBar/>
</div>
<Main>
<div className='backgr-img'>
<SearchCountry searchChange={this.onSearchChange} clickChange={this.onClickChange}/>
<SearchedCountry nations={searchedNation}/>
</div>
<Datas nations={searchedNation}/>
</Main>
<SideBar>
<Scroll className='scroll'>
<CountryList nations={nations} clickFunc/>
</Scroll>
</SideBar>
</div>
);
}
componentDidMount() {
fetch('https://restcountries.eu/rest/v2/all')
.then(response => response.json())
.then(x => this.setState({nations: x}));
}
componentDidUpdate() {
this.state.button = false;
}
}
export default App;
The countryList:
import React from 'react';
import Images from './Images';
const CountryList = ({nations, clickFunc}) => {
return (
<div className='container' style={{display: 'grid', gridTemplateColumns: 'repeat(auto-fill, minmax(115px, 3fr))'}}>
{
nations.map((country, i) => {
return (
<Images
key={country.numericCode}
name={country.name}
flag={country.flag}
clickChange={clickFunc}
/>
);
})
}
</div>
)
}
export default CountryList;
And the images.js:
import React from 'react';
import './images.css'
const Images = ({name, capital, region, population, flag, numericCode, clickChange}) => {
return (
<div className='hover bg-navy pa2 ma1 tc w10' onClick={clickChange = () => name}>
<img alt='flag' src={flag} />
<div>
<h6 className='ma0 white'>{name}</h6>
{capital}
{region}
{population}
{numericCode}
</div>
</div>
);
}
export default Images;
I had thought of using the onClick event on the single nation that was going to return the name of the clicked nation. After that I would have entered the name in the searchField and set the button to true in order to run the searchedNation function.
I thank anyone who gives me an answer in advance.
To keep the actual structure, you can try using onClickChange in Images:
onClickChange = (newName = null) => {
if(newName) {
this.setState(prevsState => ({
searchField: newName
}))
}
// old code continues
this.setState(prevsState => ({
button: true
}))
}
then in onClick of Images you call:
onClick={() => {clickChange(name)}}
Or you can try as well use react hooks (but this will require some refactoring) cause you'll need to change a property from a parent component.
With that you can use useState hook to change the value from parent component (from Images to App):
const [searchField, setSearchField] = useState('');
Then you pass setSearchField to images as props and changes the searchField value when Images is clicked:
onClick={() => {
clickChange()
setSearchField(name)
}}
I have two components, the parent and child. Currently I have these codes below. But unfortunately it returns an error:
TypeError: Cannot read property 'click' of null
For some reasons I want when button is click the Item component also will be click. But these codes below produces an error above. Anyone does know how to achieve it?
import React, { useRef } from 'react';
const App = (props) => {
const itemRef = useRef(null);
return (
<div>
{dynamicBoolean ? (
<button onClick={() => itemRef.current.click()}>
click item
</button>
) : (
//more codes here
<Item ref={itemRef} />
)}
</div>
);
};
export default App;
Child component would look like below (demonstration purposes, the code is very lengthly)
import React from 'react';
const Item = (props) => {
return (
<div>
//some design here
</div>
);
};
export default Item;
You need useRef and you have to forward this ref to the Item component.
import React, { forwardRef, useRef } from 'react';
const Item = forwardRef((props, ref) => {
return <li {...props}
onClick={() => alert('clicked on Item')}
ref={ref} >MyItem</li>
})
const App = (props) => {
const itemRef = useRef(null);
return (
<div>
<button onClick={() => itemRef.current.click()}>
click item
</button>
<Item ref={itemRef} />
</div>
);
};
export default App;
import React, { createRef } from "react";
const Hello = (props) => {
const itemRef = createRef();
const hello = () => {
itemRef.current.click();
};
return (
<div>
<button onClick={() => hello()}>click item</button>
<Item ref={itemRef} />
</div>
);
};
const Item = React.forwardRef((props, ref) => {
const myClick = () => {
console.log("this is clicked");
};
return (
<button ref={ref} className="FancyButton" onClick={myClick}>
{props.children}
</button>
);
});
export default Hello;
I have this structure
component 1
import React, { useState } from 'react'
export default function Component1() {
return (
<div>
<button onClick={handleChange}></button>
</div>
)
}
component 2
import React, { useState } from 'react'
export default function Component2() {
return (
<div>
<button onClick={handleChange}></button>
</div>
)
}
and the parent
import React from 'react'
export default function Parent() {
return (
<div>
<Component1 />
<Component2 />
</div>
)
}
The question is, how can I toggle visibility between the two, without having a button in the parent. Just the buttons inside each component. - The Component1 should be visible by default and when you press the button in Component1 it will hide it and show Component2 and vice-versa.
I've tried using useState hook on the Component1 button, but I'm not sure how to export the state and add it to the parent component.
const [showMini, setShowMini] = useState(false);
const handleChange = () => {
setShowMini(true);
}
Is this possible? or it's possible just with a button in the parent that control the two?
Thanks
Try this:
import React from 'react'
export default function Parent() {
const[show,setShow]=useState(false);
const handleChange=()=>{
setShow(!show);
}
return (
<div>
{show ? <Component2 handleChange={handleChange}/> : <Component1 handleChange={handleChange}/>}
</div>
)
}
and inside Component1 have this:
import React, { useState } from 'react'
export default function Component1({handleChange}) {
return (
<div>
<button onClick={handleChange}></button>
</div>
)
}
Similarly do it for Component2
You can do with state value and pass handleChange function ad props in the child component and in click on the button in child component call handleChange method under parent component and show hide based on state value.
import React from 'react'
const [showChild, setshowChild] = useState(false);
const handleChange = () => {
setshowChild(!showChild);
}
export default function Parent() {
return (
<div>
{showChild ? <Component2 handleChange = {handleChange}/> : <Component1 handleChange= {handleChange} />}
</div>
)
}
You can manage the state in the parent and pass down a handler to the children
import React, { useState } from 'react'
const [currentView, setCurrentView] = useState('component1')
const changeCurrentView = (view) => setCurrentView(view)
const renderViews = () => {
switch(currentView) {
case 'component1':
return <Component1 changeCurrentView={changeCurrentView} />
case 'component2':
return <Component2 changeCurrentView={changeCurrentView} />
default:
return <Component1 changeCurrentView={changeCurrentView} />
}
}
export default function Parent() {
return (
<div>
{renderViews()}
</div>
)
}
Other components
import React from 'react'
export default function Component1({ changeCurrentView }) {
return (
<div>
<button onClick={() => changeCurrentView('component1')}></button>
</div>
)
}
export default function Component2({ changeCurrentView }) {
return (
<div>
<button onClick={() => changeCurrentView('component2')}></button>
</div>
)
}
Your parent component should keep track of the state:
import React, {useState} from 'react'
export default function Parent() {
const [showChild, setShowChild] = useState(1);
const showNextChild = () => {
setShowChild( showChild === 1 ? 2 : 1 ); // set to 2 if already 1, else to 1
}
return (
<div>
{ showChild === 1 && <Component1 handleChange={showNextChild} /> }
{ showChild === 2 && <Component2 handleChange={showNextChild} /> }
</div>
)
}
A few notes:
Your components are identical, so the duplication is unnecessary, but I assume the example is just contrived.
This assumes toggling 2 components back and forth. If you have more than 2 components you are "looping" through, you can instead increment the previous showChild state and then reset it to 0 if higher than the # of components you have.
The syntax you see, showChild === 1 && <Component1 ... uses the behavior of the && operator which actually returns the 2nd item it is evaluating if both are true. In other words, const isTrue = foo && bar; sets isTrue to bar, not true as you might expect. (You know, however, that bar is "truthy" in this case, so isTrue still works in future if statements and such.) The component is always truthy, so the effect is that the component is returned if the first part is true, otherwise it is not. It's a good trick for conditionally showing components.
Try this. You can send information from child to parent with functions passed as a prop.
Parent Component:
const Parent = () => {
const [show, setShow] = useState(true);
const toggleVisibility = () => {
setShow(!show);
};
return (
<div>
{show ? (
<Child1 toggle={toggleVisibility}></Child1>
) : (
<Child2 toggle={toggleVisibility}></Child2>
)}
</div>
);
};
Child 1
const Child1 = (props) => {
const { toggle } = props;
return (
<div style={{ width: '100px', height: '100px' }}>
<button onClick={toggle}>Child 1's button</button>
</div>
);
};
Child 2
const Child2 = (props) => {
const { toggle } = props;
return (
<div style={{ width: '100px', height: '100px' }}>
<button onClick={toggle}>Child 2's button</button>
</div>
);
};