how do i to create a popup in react - javascript

This is my PopBlock.jsx file where i have my button in and i need to be able to click the button and open the popup where all the information is another file Popup.jsx how could i make this pop up work
import React, {useContext, useEffect, useState} from 'react';
import PropTypes from 'prop-types';
import styles from './PopupBlock.module.scss';
import {getImageProps, getValue} from '../../../utils';
import {ServiceContext} from '../../Services';
import Popup from './Popup';
const PopupBlock = ({block}) => {
const services = useContext(ServiceContext);
const [imageProps, setImageProps] = useState(null);
const [open, setOpen] = useState(false);
useEffect(() => {
if (block) {
setImageProps(getImageProps(
block.image,
null,
url => services.content.getFileURL(url)
));
} else {
setImageProps(null);
}
}, [block, services]);
return (
<div className={styles.container}>
<div className={styles.content}>
{getValue(block.title, null, (title) => (<h3>{title}</h3>))}
{getValue(block.text, null, (text) =>
(<button onClick={() => setOpen(true)}>{text}</button>))}
</div>
{imageProps ? (
<div className={styles.image}>
<img alt="Alt text populated from Acoustic" {...imageProps} />
</div>
) : null}
<div className={[styles.popup, /*styles.visible*/].join(' ')}>
<Popup block={block}/>
</div>
</div>
);
}
;
PopupBlock.propTypes =
{
block: PropTypes.object
}
;
export default PopupBlock;
This is my Popup.jsx file where the inner information of the pop up is
import React, {useContext, useEffect, useState} from 'react';
import PropTypes from 'prop-types';
import styles from './Popup.module.scss';
import {getImageProps, getValue} from '../../../utils';
import {ServiceContext} from '../../Services';
import Html from '../../Html';
const Popup = ({block}) => {
const services = useContext(ServiceContext);
const [imageProps, setImageProps] = useState(null);
useEffect(() => {
if (block) {
setImageProps(getImageProps(
block.image,
null,
url => services.content.getFileURL(url)
));
} else {
setImageProps(null);
}
}, [block, services]);
return (
<div className={styles.background}>
<div className={styles.container}>
{imageProps ? (
<div className={styles.image}>
<img alt="Alt text populated from Acoustic" {...imageProps} />
</div>
) : null}
<div className={styles.content}>
<div className={styles.section}>
{getValue(block.title, null, (title) => (<h3>{title}</h3>))}
{getValue(block.content, null, (content) => (<Html value={content}/>))}
</div>
<div className={styles.info}>
{getValue(block.information, null, (information) => (<Html value={information}/>))}
</div>
</div>
</div>
</div>
);
}
;
Popup.propTypes =
{
block: PropTypes.object
}
;
export default Popup;
if anyone knows how i could make a pop up for this please let me know thanks the blocks are what have the button on and when they are pressed i want the pop up with all the information in to come up

Related

Update child component while using filter on parent component in React

I have seen this asked before but I can't seem to be able to wrap my head around it with my situation.
I am using a search bar to filter the data down and it works but the image will not update. The URL passing to the child works fine but it's just not changing its state. I just don't really understand how to implement it.
PokemonList.jsx
import axios from "axios";
import React, { useEffect, useState } from "react";
import PokemonSprite from "./PokemonSprite";
import Card from "#material-tailwind/react/Card";
import CardBody from "#material-tailwind/react/CardBody";
import CardFooter from "#material-tailwind/react/CardFooter";
import H6 from "#material-tailwind/react/Heading6";
import Paragraph from "#material-tailwind/react/Paragraph";
import Button from "#material-tailwind/react/Button";
// const baseURL = "https://pokeapi.co/api/v2/pokemon?limit=898";
const baseURL = "https://pokeapi.co/api/v2/pokemon?limit=20";
export default function PokemonList() {
const [post, setPost] = useState([]);
const [searchTerm, setSearchTerm] = useState('');
useEffect(() => {
axios.get(baseURL).then((response) => {
setPost(response.data.results);
});
}, []);
if (!post) return <p>Sorry, no results.</p>;
return (
<div>
<input type="text" placeholder="Search..." onChange={e => {setSearchTerm(e.target.value)}}/>
{post.filter((data) => {
if (searchTerm == "") {
return data;
} else if (data.name.toLowerCase().includes(searchTerm.toLowerCase())) {
console.log(data);
return data;
}
}).map((data, idx) => (
<div className="p-5">
<Card key={idx}>
<PokemonSprite url={data.url} />
<CardBody>
<H6 color="gray">{data.name}</H6>
<Paragraph color="gray">
Don't be scared of the truth because we need to restart the human
foundation in truth And I love you like Kanye loves Kanye I love
Rick Owens’ bed design but the back is...
</Paragraph>
</CardBody>
<CardFooter>
<Button color="lightBlue" size="lg" ripple="light">
Read More
</Button>
</CardFooter>
</Card>
</div>
))}
</div>
);
}
PokemonSprite.jsx
import axios from "axios";
import React, { useEffect, useState } from "react";
import CardImage from "#material-tailwind/react/CardImage";
export default function PokemonList(url) {
const [post, setPost] = useState();
console.log(url);
useEffect(() => {
axios.get(url.url).then((response) => {
//console.log(response.data);
setPost(response.data);
});
}, []);
if (!post) return <p>Sorry, no results.</p>;
return (
<div>
<CardImage
src={post.sprites.front_default}
alt="Card Image"
/>
</div>
);
}
Please rewrite your PokemonSprite component like this to enable re rendering on updates to the Url...
import axios from "axios";
import React, { useEffect, useState } from "react";
import CardImage from "#material-tailwind/react/CardImage";
export default function PokemonList(url) {
const [post, setPost] = useState();
console.log(url);
const getUpdatedImage = async (imageUrl) => {
const response = await axios.get(imageUrl);
setPost(response.data);
return post;
}
useEffect(() => {
getUpdatedImage(url.url);
}, [url]);
if (!post) return <p>Sorry, no results.</p>;
return (
<div>
<CardImage
src={post.sprites.front_default}
alt="Card Image"
/>
</div>
);
}

React.js Display a component with onClick event

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)
}}

Can I fix the issue when I call an api, it called two times with reactjs?

I used redux-saga and I want when I click on my button, the api will be fetching,
My code is:
// #flow
import React, { useEffect, useState, useRef } from 'react';
import { connect } from 'react-redux';
import { Row, Col, Card, CardBody, Button, ButtonDropdown, Dropdown, DropdownToggle, DropdownMenu, DropdownItem } from 'reactstrap';
import { Translate } from 'src/components';
import { VCS } from 'src/common';
import { ACCESS_LEVELS, USER_RIGHTS, userAccess } from 'src/constants/user-rights';
import * as Actions from './actions';
import ClientUsersRSuiteTable from './components/client-users-rsuite-table';
import './users.scss';
function Users({ clientId, clientUsers, requestClientUsersData, getUserTemplate, pageParameters, ...props }) {
const [searchValue, setSearchValue] = useState('');
useEffect(() => {
requestClientUsersData({ id: clientId, pageParams: null });
}, []);
const handleChangeSearchValue = (input) => {
const search = input != '' ? input : null;
setSearchValue(search);
};
const [dropdownOpen, setDropdownOpen] = useState(false);
const toggle = () => setDropdownOpen(prevState => !prevState);
return (
<>
<VCS hasRights={[userAccess(ACCESS_LEVELS.EDIT, USER_RIGHTS.API_CLIENTS)]}>
<div className="row">
<div className="col">
<Button
style={{ backgroundColor: '#ffffff !important', color: '#fa5c7c !important' }}
outline
color="danger"
className="mb-2 mr-1 btn-user-template"
onClick={() => getUserTemplate(clientId)}
>
<i className="mdi mdi-file-outline mr-1" size="large" />
<Translate id="pages.client.users.get.user.template" />
</Button>
</div>
</div>
</div>
</VCS>
</>
);
}
Users.defaultProps = {
};
const mapStateToProps = (state) => ({
clientUsers: state.Administration.users.clientUsers ? state.Administration.users.clientUsers :
state.Administration.clients.clientUsers,
pageParameters: state.Administration.users.clientUsersPageParameters ? state.Administration.users.clientUsersPageParameters :
state.Administration.clients.clientUsersPageParameters
});
export default connect(mapStateToProps, Actions)(Users);
My api is:
export const getUserTemplate = async (clientId) => request(`api/clients/${clientId}/users/import/template`, 'GET');
When I click on the button, my api is called two times with same response !
The Api is to export excel data, when I run it, I get :
I want when I run it on clicking the button, I get just one file not two(because it runs two time)
How can I fix it ?

React.js: How to pass props to components?

I want to make API call reusable and use it's functionality in other components. I am fetching API in Hook/index.js component. Then I want to iterate through data got from API in another component, make it as a parameter and use it in other components.
I've a Flag/index.js component for flag(img) and want to get url of the image as a parameter and use in Flag/index.js component.
Any help will be appreciated.
In Hook/index.js I fetch API
import React, { useState, useEffect } from "react";
import "./hook.scss";
export default function Hook(){
const [data, setData] = useState([]);
const [error, setError] = useState(null);
const [search, setSearch] = useState("");
const fetchData = () => {
fetch("https://restcountries.eu/rest/v2/all")
.then((res) => res.json())
.then((result) => setData(result))
.catch((err) => console.log("error"));
};
useEffect(() => {
const searchResult = data && data.filter((item) => item.name.toLowerCase().includes(search));
setSearch(searchResult);
}, []);
useEffect(() => {
fetchData();
}, []);
return [data, error];
}
In App.js I'm mapping through API data
import React, { useState }from "react";
import Header from "./components/Header";
import SearchBar from "./components/SearchBar";
import Flag from "./components/Flag";
import useCountries from "./Hooks";
import "./App.scss";
export default function App () => {
const [data, error] = useCountries();
return (
<div className="App">
<SearchBar />
<Header />
{data &&
data.map((country) => (
<div className="CountryList" key={country.name}>
<div className="CountryListImg">
<img src={country.flag} alt="" width="80px" /> if change here to flag={country.flag} I see link in browser
</div>
<div className="countryName">{country.name}</div>
<div className="population">{country.population}</div>
<div className="region">{country.region}</div>
<div>
{country.languages.map((language, languageIndex) => (
<div key={languageIndex}>{language.name}</div>
))}
</div>
</div>
))}
<useCountries />
</div>
);
return [data, error]
}
And my Flag/index.js component, of course doesn't work
import React from "react";
import "./flag.scss";
export default function Flag({flag}) {
return (
<div className="">
<img src={flag} alt="" width="80px" />
</div>
);
};
How to make work search bar. For now it says undefined in console
import React, {useState} from "react";
import "./SearchBar.scss";
export default function Searchbar({data}) {
const [search, setSearch] = useState("");
function handleChange(e) {
setSearch(e.target.value);
}
console.log(data)
return (
<div className="SearchBar">
<input
className="input"
type="text"
placeholder="search country ..."
value={data}
onChange={handleChange}
/>
{data && data.filter((item) => item.name.toLowerCase().includes(search))}
</div>
);
};
You need to use Flag component inside App component
import React, { useState }from "react";
import Header from "./components/Header";
import SearchBar from "./components/SearchBar";
import Flag from "./components/Flag";
import useCountries from "./Hooks";
import "./App.scss";
export default () => {
const [data, error] = useCountries();
return (
<div className="App">
<SearchBar />
<Header />
{data &&
data.map((country) => (
<div className="CountryList" key={country.name}>
<div className="CountryListImg">
<Flag flag={country.flag}/>
</div>
<div className="countryName">{country.name}</div>
<div className="population">{country.population}</div>
<div className="region">{country.region}</div>
<div>
{country.languages.map((language, languageIndex) => (
<div key={languageIndex}>{language.name}</div>
))}
</div>
</div>
))}
<useCountries />
</div>
);
return [data, error]
}
1
I am not completely sure if this answers the question, but to add the abstracted Flag class in you simply need to change this:
<img src={country.flag} alt="" width="80px" />
To this:
<Flag flag={country.flag} />
Just use <Flag flag={country.flag} />
See online: https://stackblitz.com/edit/stackoverflow-63831710?file=src/App.js

React-router not rendering dynamic component- When clicked nothing happens

I am doing a small project and have a list of components that display information about countries. Now I have added react router so that when I click on a card it displays more information about that country. Now when I click on the card nothing happens! Below is the code for the Countries.
import React, { Component } from 'react';
import { CountryList } from './Components/Card-List/CountryList';
import { SearchBox } from './Components/Search-box/Search-Box';
import './Countries.styles.css';
import { DetailCountryCard } from './Components/DetailCountryCard/DetailCountryCard';
import { BrowserRouter as Router, Switch, Route } from 'react-router-dom';
class Countries extends Component {
constructor() {
super();
this.state = {
countries:[],
searchField:"",
regionField:"",
darkMode: false
}
this.setDarkMode = this.setDarkMode.bind(this);
};
componentDidMount() {
fetch("https://restcountries.eu/rest/v2/all")
.then(response => response.json())
.then(all => this.setState({ countries: all,
regions: all}))
.catch(error => console.log("I have errored" + error));
}
setDarkMode(e){
this.setState((prevState) => ({ darkMode: !prevState.darkMode }));
}
render() {
const { countries, searchField, regionField, darkMode } = this.state;
const filterCountries = countries.filter((country) => country.name.toLowerCase().includes(searchField.toLowerCase()) &&
country.region.toLowerCase().includes(regionField.toLowerCase()));
return(
<Router>
<div className={darkMode ? "dark-mode" : "light-mode" }>
<nav className="navbar-items">
<h1 className="header">Where in the World</h1>
<div className="moon-end">
<button onClick={this.setDarkMode}>
<i className={darkMode ? "moon fas fa-moon" : "moon far fa-moon" }></i>
</button>
<h2>{darkMode ? "Dark Mode" : "Light Mode" }</h2>
</div>
</nav>
<div className="Input">
< SearchBox type="search" placeholder="Search a Country" handlechange={e=> this.setState({
searchField: e.target.value })}
/>
< SearchBox type="regions" placeholder="Filter by Regions" handlechange={e=> this.setState({
regionField: e.target.value })}
/>
</div>
<CountryList countries={filterCountries} />
{/* <Route path="/" exact component={Countries} /> */}
<Switch>
<Route path="/card-detail/:name" component={ DetailCountryCard } exact/>
</Switch>
</div>
</Router>
);
}
}
export default Countries
The link for each card is in the following component:
import React from 'react';
import './CountryList.styles.css';
import {Link} from 'react-router-dom'
import { CountryCard } from '../Card/CountryCard';
export const CountryList = (props) => (
<div className='card-list'>
{props.countries.map(country => (
<Link to={`/card-detail/${country.name}`} >
<CountryCard key={country.alpha2Code} country={country} />
</Link>
))}
</div>
);
This should go to the following component:
import React from 'react';
import { useEffect } from 'react';
import { useState } from 'react';
export const DetailCountryCard = ({match}) => {
useEffect(() => {
fetchItem();
console.log(match);
},[])
const [country, setCountry] = useState([])
const fetchItem = async ()=> {
const fetchCountry = await fetch(`https://restcountries.eu/rest/v2/name/${match.params.name}`);
const countries = await fetchCountry.json();
setCountry(countries);
console.log(country);
}
return (
<div>
{country.map(town => (
<div>
<h1 key={town.alpha2Code}>{town.name}</h1>
<p>Native Name{town.nativeName}</p>
<p>Region: {town.region}</p>
<p>Languages: {town.languages[0].name}</p>
</div>
))}
</div>
);
}
Not sure what I am missing. I don't think I have done a typo on the component. So not sure why it is not rendering? Any help would be appreciated.
You just need add dependency of match in useEffect in DetailCountryCard. Because [] its similar in Class ComponentcomponentDidMount()` and you need to listen when match it's changed.
This is final code to DetailCountryCard:
import React from "react";
import { useEffect } from "react";
import { useState } from "react";
export const DetailCountryCard = ({ match }) => {
useEffect(() => {
fetchItem();
console.log(match);
}, [match]);
const [country, setCountry] = useState([]);
const fetchItem = async () => {
const fetchCountry = await fetch(
`https://restcountries.eu/rest/v2/name/${match.params.name}`
);
const countries = await fetchCountry.json();
setCountry(countries);
console.log(country);
};
return (
<div>
{country.map(town => (
<div>
<h1 key={town.alpha2Code}>{town.name}</h1>
<p>Native Name{town.nativeName}</p>
<p>Region: {town.region}</p>
<p>Languages: {town.languages[0].name}</p>
</div>
))}
</div>
);
};
I tested in CodeSandBox and it works!
Link

Categories