I'v a problem because I would split one component in two components.
I build my component with a search bar with autocomplete and the map with a pin chosen with the search bar. And me, I would one component with searcbar and an other with map who comunicate the date between them.
But I don't no how make this ..
Somebody could help me ?
Sorry for my Enlish, I'm French
Here is the code of my component:
import React, { useState, createContext } from 'react';
import {Map, Marker, GoogleApiWrapper} from 'google-maps-react';
import PlacesAutocomplete, {
geocodeByAddress,
getLatLng,
} from 'react-places-autocomplete';
import Child2 from '../Child2';
const Location = createContext();
function Searchbar() {
const [address, setAdress] = useState("")
const [coordinates, setCoordinates] = useState({
lat: null,
lng: null
})
const handleSelect = (value, props) => {
const results = await geocodeByAddress(value);
const ll = await getLatLng(results[0])
console.log(ll)
setAdress(value)
setCoordinates(ll)
props.OnSelectPlace(ll)
}
return (
<div className="Searchbar">
<p>{coordinates.lat}</p>
<p>{coordinates.lng}</p>
<p>{address}</p>
<PlacesAutocomplete
value={address}
onChange={setAdress}
onSelect={handleSelect}
>
{({ getInputProps, suggestions, getSuggestionItemProps, loading }) => (
<div>
<input
{...getInputProps({
placeholder: 'Search Places ...',
className: 'location-search-input',
})}
/>
<div className="autocomplete-dropdown-container">
{loading && <div>Loading...</div>}
{suggestions.map(suggestion => {
const className = suggestion.active
? 'suggestion-item--active'
: 'suggestion-item';
// inline style for demonstration purpose
const style = suggestion.active
? { backgroundColor: '#E4E4E4', cursor: 'pointer' }
: { backgroundColor: '#F8F8F8', cursor: 'pointer' };
return (
<div
{...getSuggestionItemProps(suggestion, {
className,
style,
})}
>
<span>{suggestion.description}</span>
</div>
);
})}
</div>
</div>
)}
</PlacesAutocomplete>
</div>
);
}
export {Location};
export default GoogleApiWrapper({
apiKey: ("secret_code_api_google_map")
})(Searchbar)
Build two separate components one for the map and other for the searchbar. Then pass date as a prop to the second component.
Related
By using console.log(responseData.places) I have checked the fetching works since I am using a hook for this and seems to work fine until I setLoadedPlaces with is the method I use to update the loadedPlaces which I later use to get the values to fill the frontend part of the website.
This is the output I get from this console.log I did and the values are correct.
[{…}]
0: address: "sis se puede
busrespect: 'tu puedes',
creator: "6384e2f543f63be1c560effa"
description: "al mundial"
id: "6384e30243f63be1c560f000"
image:"https://upload.wikimedia.org/wikipedia/commons/thumb/1/10/Empire_State_Building_%28aerial_view%29.jpg/400px-Empire_State_Building_%28aerial_view%29.jpg"location: {lat: -12.086158, lng: -76.898019}
title: "Peru"
__v: 0
_id: "6384e30243f63be1c560f000"[[Prototype]]:
Objectlength: 1[[Prototype]]: Array(0)
So after this this the code I have in the frontend (SINCE the backend works properly) Let me know if you have any doubts with this logic
This is UserPlaces.js
import React, {useState, useEffect } from 'react';
import PlaceList from '../components/PlaceList';
import { useParams } from 'react-router-dom';
import { useHttpClient } from '../../shared/hooks/http-hook';
import ErrorModal from '../../shared/components/UIElements/ErrorModal';
import LoadingSpinner from '../../shared/components/UIElements/LoadingSpinner';
const UserPlaces = () => {
const {loadedPlaces, setLoadedPlaces} = useState();
const {isLoading, error, sendRequest, clearError } = useHttpClient();
const userId = useParams().userId;
useEffect(() => {
const fetchPlaces = async () => {
try {
const responseData = await sendRequest(
`http://localhost:5000/api/places/user/${userId}`
);
console.log(responseData.bus_stops)
setLoadedPlaces(responseData.bus_stops);
} catch (err) {}
};
fetchPlaces();
}, [sendRequest, userId]);
return (
<React.Fragment>
<ErrorModal error={error} onClear={clearError} />
{isLoading && (
<div className="center">
<LoadingSpinner />
</div>
)}
{!isLoading && loadedPlaces && <PlaceList items={loadedPlaces} />}
</React.Fragment>
);
};
export default UserPlaces;
This is Place-List.js
import React from 'react';
import "./PlaceList.css"
import Card from '../../shared/components/UIElements/Card'
import PlaceItem from './PlaceItem';
import Button from '../../shared/components/FormElements/Button';
const PlaceList = props => {
if (props.items.length === 0) {
return (
<div className='place-list-center'>
<Card>
<h2>No bus stops available. Be the first one to create one!</h2>
<Button to='/places/new'> Create Bus Stop </Button>
</Card>
</div>
);
}
return (
<ul className="place-list">
{props.items.map(bus_stops => (
<PlaceItem
key={bus_stops.id}
id={bus_stops.id}
image={bus_stops.image}
title={bus_stops.title}
busrespect={bus_stops.busrespect}
description={bus_stops.description}
address={bus_stops.address}
creatorId={bus_stops.creator}
coordinates={bus_stops.location}
/>
))}
</ul>
);
};
export default PlaceList;
This is PlaceItem.js
import React, { useState } from 'react';
import { useContext } from 'react';
import Card from '../../shared/components/UIElements/Card';
import Button from '../../shared/components/FormElements/Button';
import Modal from '../../shared/components/UIElements/Modal';
import Map from '../../shared/components/UIElements/Map';
import {AuthContext} from '../../shared//context/auth-context'
import "./PlaceItem.css";
const PlaceItem = props => {
const auth = useContext(AuthContext);
const [showMap, setShowMap] = useState(false);
const [showConfirmModal, setShowConfirmModal] = useState(false);
const openMapHandler = () => setShowMap(true);
const closeMapHandler = () => setShowMap(false);
const showDeleteWarningHandler = () => {
setShowConfirmModal(true);
};
const cancelDeleteHandler = () => {
setShowConfirmModal(false);
};
const confirmDeleteHandler = () => {
setShowConfirmModal(false); //when clicked close the new Modal
console.log('DELETING...');
};
return (
<React.Fragment>
<Modal show={showMap}
onCancel={closeMapHandler}
header={props.address}
contentClass="place-item__modal-content"
footerClass="place-item__modal-actions"
footer={<Button onClick={closeMapHandler}>Close </Button>}
>
<div className='map-container'>
<Map center={props.coordinates} zoom={16}/> {/* Should be props.coordinates but we writing default data for now until geocoding solved. */}
</div>
</Modal>
<Modal
show={showConfirmModal}
onCancel={cancelDeleteHandler}
header="Are you entirely sure?"
footerClass="place-item__modal-actions"
footer={
<React.Fragment>
<Button inverse onClick={cancelDeleteHandler}>
CANCEL
</Button>
<Button danger onClick={confirmDeleteHandler}>
DELETE
</Button>
</React.Fragment>
}
>
<p>
Do you want to proceed and delete this place? Please note that it
can't be undone thereafter.
</p>
</Modal>
<li className='"place=item'>
<Card className="place-item__content">
<div className='place-item__image'>
<img src={props.image} alt={props.title}/>
</div>
<div className='place-item__info'>
<h2>{props.title}</h2>
<h3>{props.address}</h3>
<p>{props.description}</p>
<p>{props.busrespect}</p>
</div>
<div className='place-item__actions'>
<Button inverse onClick={openMapHandler}> VIEW ON MAP</Button>
{auth.isLoggedIn && (<Button to={`/places/${props.id}`}> EDIT</Button> )}
{auth.isLoggedIn &&<Button danger onClick={showDeleteWarningHandler}> DELETE </Button>}
</div>
</Card>
</li>
</React.Fragment>
);
};
export default PlaceItem;
This is auth-context:
import { createContext } from "react";
export const AuthContext = createContext({
isLoggedIn: false,
userId: null,
login: () => {},
logout: () => {}});
This is is Modal.js
import React from 'react';
import ReactDOM from 'react-dom';
import Backdrop from './Backdrop';
import { CSSTransition } from 'react-transition-group';
import './Modal.css';
const ModalOverlay = props => {
const content =(
<div className={`modal ${props.className}`} style = {props.style}>
<header className={`modal__header ${props.headerClass}`}>
<h2>{props.header}</h2>
</header>
<form
onSubmit={
props.onSubmit ? props.onSubmit : event => event.preventDefault()
}
>
<div className={`modal__content ${props.contentClass}`}>
{props.children}
</div>
<footer className={`modal__content ${props.footerClass}`}>
{props.footer}
</footer>
</form>
</div>
);
return ReactDOM.createPortal(content, document.getElementById('modal-hook'));
};
const Modal = props => {
return (
<React.Fragment>
{props.show && <Backdrop onClick={props.onCancel} />}
<CSSTransition in={props.show}
mountOnEnter
unmountOnExit
timeout={200}
classNames="modal"
>
<ModalOverlay {...props}/>
</CSSTransition>
</React.Fragment>
);
};
export default Modal;
Also Trust the routing is correct since I have checked it already and I am just wondering if the logic in REACT with loadedPlaces, PlaceItema and PlaceList makes sense and it working. Let me know please. It will be really helpful.
Summary: Not getting any error but no visual data appears in the scren just the header of my website and the background (rest is empty) even though logic is functional.
const {loadedPlaces, setLoadedPlaces} = useState();
change the above line to
const [loadedPlaces, setLoadedPlaces] = useState();
I have a list of products in a react component:
I am trying to display a modal when I click on the specific product that displays the product information through a graphql query. The query works fine. I'm just having problems displaying the modal.
Nothing happens when I click except my console.log statement. DisplayProduct never really gets loaded except when the page is loaded. How do I trigger a reload of DisplayProduct on click.
I'm pretty new to react, hence the issue...
ProductList.jsx
/* eslint-disable no-unused-vars */
import { useState, useCallback } from "react";
import { ResourceList, TextStyle, Stack, Thumbnail } from "#shopify/polaris";
import { Layout } from "#shopify/polaris";
import { DisplayProduct } from "./DisplayProduct";
export function ProductsList({ data }) {
const [open, setOpen] = useState(false);
const [clickedItem, setClickedItem] = useState(false);
const [active, setActive] = useState(false);
return (
<Layout>
<ResourceList // Defines your resource list component
showHeader
resourceName={{ singular: `Product`, plural: `Products` }}
items={data.nodes}
renderItem={(item) => {
const media = (
<Thumbnail
source={
item.images.edges[0] ? item.images.edges[0].node.originalSrc : ``
}
alt={item.images.edges[0] ? item.images.edges[0].node.altText : ``}
/>
);
const price = item.variants.edges[0].node.price;
return (
<ResourceList.Item
id={item.id}
media={media}
accessibilityLabel={`View details for ${item.title}`}
onClick={() => {
setClickedItem(item.id);
setOpen(true);
console.log(`itemId`, clickedItem);
}}
onCancel={() => setOpen(false)}
>
<Stack>
<Stack.Item fill>
<h3>
<TextStyle variation="strong">{item.title}</TextStyle>
</h3>
</Stack.Item>
<Stack.Item>
<p>${price}</p>
</Stack.Item>
</Stack>
</ResourceList.Item>
);
}}
/>
<Layout.Section>
<div style={{ height: `500px` }}>
<Modal
onClose={() => {
setActive(false);
setOpen(false);
console.log(`clicked close button: `, { active });
}}
title="Display Product"
open={active}
loading={false}
limitHeight={false}
sectioned={true}
show={active}
>
<Modal.Section>
<TextContainer>
<DisplayProduct productId={clickedItem} />
</TextContainer>
</Modal.Section>
</Modal>
</div>
</Layout.Section>
</Layout>
);
}
DisplayProduct.jsx
/* eslint-disable no-unused-vars */
import { useState, useCallback } from "react";
import { Banner, Button, Modal, TextContainer } from "#shopify/polaris";
import { gql, useQuery } from "#apollo/client";
import { Loading } from "#shopify/app-bridge-react";
const GET_PRODUCT_BY_ID = gql`
query getProductsById($id: ID!) {
product(id: $id) {
createdAt
defaultCursor
description
descriptionHtml
vendor
}
}
`;
export function DisplayProduct({ productId }) {
const [active, setActive] = useState(true);
const { loading, error, data, refetch } = useQuery(GET_PRODUCT_BY_ID, {
variables: { id: productId },
});
if (loading || typeof data === `undefined`) {
console.log(`loading`);
return <Loading />;
}
if (error) {
console.warn(error);
return (
<Banner status="critical">There was an issue loading products.</Banner>
);
}
console.log(`productId: `, productId);
console.log(`data: `, data);
return (
<p>
display our product here {data.id} and {productId}
</p>
);
}
New to react is right!
I needed a conditional display for the modal. If open is true, display the modal. When the modal is closed, the dom element is removed and then reloaded when open is set to true. I'm not sure if I can refine that down even further so it doesn't have to rebuild the modal each time a product is clicked
Here is the complete .jsx:
import { useState, useCallback } from "react";
import {
ResourceList,
TextStyle,
Stack,
Thumbnail,
Button,
Modal,
TextContainer,
Layout,
} from "#shopify/polaris";
import { DisplayProduct } from "./DisplayProduct";
export function ProductsList({ data }) {
const [open, setOpen] = useState(false);
const [clickedItem, setClickedItem] = useState(false);
const { prodData, setProdData } = useState(false);
return (
<Layout>
<ResourceList // Defines your resource list component
showHeader
resourceName={{ singular: `Product`, plural: `Products` }}
items={data.nodes}
renderItem={(item) => {
const media = (
<Thumbnail
source={
item.images.edges[0] ? item.images.edges[0].node.originalSrc : ``
}
alt={item.images.edges[0] ? item.images.edges[0].node.altText : ``}
/>
);
const price = item.variants.edges[0].node.price;
return (
<ResourceList.Item
id={item.id}
media={media}
accessibilityLabel={`View details for ${item.title}`}
onClick={() => {
setClickedItem(item.id);
setOpen(true);
}}
onCancel={() => setOpen(false)}
>
<Stack>
<Stack.Item fill>
<h3>
<TextStyle variation="strong">{item.title}</TextStyle>
</h3>
</Stack.Item>
<Stack.Item>
<p>${price}</p>
</Stack.Item>
</Stack>
</ResourceList.Item>
);
}}
/>
<Layout.Section> /* here is the important part. Conditionally load the modal and therefore, DisplayProduct. */
{open ? (
<div style={{ height: `500px` }}>
<Modal
onClose={() => {
setOpen(false);
}}
title="Display Product"
open={open}
loading={false}
limitHeight={false}
sectioned={true}
>
<Modal.Section>
<TextContainer>
<DisplayProduct productId={clickedItem} />
</TextContainer>
</Modal.Section>
</Modal>
</div>
) : (
``
)}
</Layout.Section>
</Layout>
);
}
I have an application in react js, there is a list of elements and when the user hover over the element the background color should change to red. At the moment it works. Also i want when i leave the element unhover, the element should be without red background, but now when i leave the element the color does not dissappear. Basically the hover should affect only that element where the user hover.
import "./styles.css";
import React, { memo, useEffect, useState } from "react";
const Element = ({ id }) => {
const styles = {
background: "red"
};
const [isHovered, setIsHovered] = useState(false);
return (
<div
className={isHovered ? "hovered" : ""}
onMouseOver={() => {
setIsHovered(true);
}}
>
hello {id}
</div>
);
};
export default function App() {
return (
<div className="App">
{[0, 1, 2].map((el) => {
return <Element key={el} id={el} />;
})}
</div>
);
}
demo: https://codesandbox.io/s/compassionate-einstein-9v1ws?file=/src/App.js:0-557 Question: Who can help to solve the issue?
Instead of onMouseOver, you can use onMouseEnter and onMouseLeave to apply the styles based on the isHovered state.
Here is your updated code.
import "./styles.css";
import React, { memo, useEffect, useState } from "react";
const Element = ({ id }) => {
// const styles = {
// background: "red"
// };
const [isHovered, setIsHovered] = useState(false);
console.log({ isHovered });
return (
<div
// style={{ backgroundColor: isHovered ? "red" : "transparent" }}
className={"hovered"}
// onMouseEnter={() => setIsHovered(true)}
// onMouseLeave={() => setIsHovered(false)}
>
hello {id}
</div>
);
};
export default function App() {
return (
<div className="App">
{[0, 1, 2].map((el) => {
return <Element key={el} id={el} />;
})}
</div>
);
}
You can also do the same thing using the className without using the state variable, with just targeting the hover class. I've used the both in the sandbox.
In case of doing only with className, you can just add a className to the div
className={"hovered"}
then, update the styles of the class in the css file.
.hovered:hover {
background: red;
}
Updated sandbox
I am trying to create a Carousel of images and embedded Youtube videos (the first item is a youtube video and the rest are images)
I am using the React Responsive Carusel npm package but I can't find a way to create the carousel I want without bugs.
I saw a solution over here but it's only for videos.
Here is my code at the moment:
import React from 'react'
import 'react-responsive-carousel/lib/styles/carousel.min.css' // requires a loader
import { Carousel } from 'react-responsive-carousel'
import ReactPlayer from 'react-player'
const ModalImagesPreview = () => {
return (
<div>
<Carousel>
<div>
<ReactPlayer
url='https://www.youtube.com/watch?v=ysz5S6PUM-U'
volume='1'
muted
width='100%'
playing={true}
/>
</div>
<div>
<img src='https://cdn.dribbble.com/users/2146089/screenshots/12387473/media/bf9ffb522fe68ba57ebdc62bc3f16cc5.png' />
</div>
<div>
<img src='https://cdn.dribbble.com/users/427857/screenshots/12318706/media/f30f662dbf160022412812881b2afb43.jpg' />
</div>
</Carousel>
</div>
)
}
export default ModalImagesPreview
Thanks for the helpers!
Here is what I found on the react-responsive-carousel website
import 'react-responsive-carousel/lib/styles/carousel.min.css'; // requires a loader
import React from 'react';
import ReactPlayer from 'react-player';
import { Carousel } from 'react-responsive-carousel';
import { PropTypes } from 'prop-types';
import { Grid, makeStyles } from '#material-ui/core';
const DUMMY_VIDEOS = [
{
_id: '5fd025a181e2c80897c14ae1',
videoUrl: 'https://www.youtube.com/embed/AVn-Yjr7kDc'
}
];
const useStyles = makeStyles(theme => ({
carousel: {
margin: theme.spacing(2)
}
}));
const YoutubeSlide = ({ url, isSelected }) => (
<ReactPlayer width="100%" height="276px" url={url} playing={isSelected} />
);
const CarouselVideo = ({ data }) => {
const classes = useStyles();
const customRenderItem = (item, props) => (
<item.type {...item.props} {...props} />
);
const getVideoThumb = videoId =>`https://img.youtube.com/vi/${videoId}/default.jpg`;
const getVideoId = url =>url.substr('https://www.youtube.com/watch?v='.length, url.length);
const customRenderThumb = children =>
children.map(item => {
const videoId = getVideoId(item.props.url);
return <img key={videoId} src={getVideoThumb(videoId)} />;
});
return (
<Grid item md={6} xs={12}>
<Carousel
autoPlay={false}
className={classes.carousel}
emulateTouch={true}
showArrows={true}
showThumbs={true}
showStatus={false}
infiniteLoop={true}
renderItem={customRenderItem}
renderThumbs={customRenderThumb}
>
{data.map(v => (
<YoutubeSlide
url={v.videoUrl}
muted
playing={false}
key={v._id ? v._id : v.id}
/>
))}
</Carousel>
</Grid>
);
};
YoutubeSlide.propTypes = {
url: PropTypes.string,
isSelected: PropTypes.bool
};
CarouselVideo.propTypes = {
data: PropTypes.array
};
CarouselVideo.defaultProps = {
data: DUMMY_VIDEOS
};
export default CarouselVideo;
Last step is to call this CarouselVideo where you want to render with your data as props or just call it without passint anythind => this will display the dummy videos I added there.
So you can call it like the below:
// DisplayVideo.js file with default videos as shown below
const DisplayVideo = () => {
render( <CarouselVideo />)
}
// or with your data
const DisplayVideo = ({PASS_YOU_DATA_HERE}) => {
render( <CarouselVideo data={PASS_YOU_DATA_HERE} />)
}
Result:
(source: https://www.npmjs.com/package/react-responsive-carousel)
I have two components in the same .js file using React.createContext() and React.useContext(). Now I would like to move the Menu component in another .js file but I'm having trouble using the same context. This is the code:
const ManagerContext = React.createContext(null);
export default function LessonManager() {
const [title, setTitle] = React.useState('SomeOtherTitle');
const [editing, toggleEditor] = React.useState(false);
const value = React.useMemo(() => {
return {
title,
setTitle,
editing,
toggleEditor,
log: (t) => console.log(t)
}
}, [title, editing]);
return (
<ManagerContext.Provider value={value}>
<div className='box-default expand'>
<div className='handle' style={{display: 'flex', justifyContent: 'center', width: '100%', cursor: 'grab'}}>
<LessonMenu/>
</div>
</div>
</ManagerContext.Provider>
)
}
export function LessonMenu() {
const state = React.useContext(ManagerContext);
return (
<ButtonGroup size='sm'>
<IconButton
className='rsuite-btn menu-button'
onClick={()=>state.toggleEditor(!state.editing)}
icon={ <Icon icon={state.editing ? ('eye') : ('edit2')} />} />
</ButtonGroup>
)
}
I tried to export the const ManagerContext:
export const ManagerContext = React.createContext(null);
and import it in the Menu.js file I created with the Menu component, but it doesn't work. What am I doing wrong?
Your code seems to work, note how you need to import the context:
// export const ManagerContext = React.createContext(null);
import { ManagerContext } from "./Menu.js";
export function LessonMenu() {
const { toggleEditor, editing } = React.useContext(ManagerContext);
return (
<ButtonGroup size="sm">
<IconButton
className="rsuite-btn menu-button"
onClick={() => toggleEditor(!editing)}
icon={<Icon icon={toggleEditor ? "eye" : "edit2"} />}
/>
</ButtonGroup>
);
}