React Tooltip using Components - javascript

I have a google maps widget on my page with markers on it to represent specific locations. I have made it so that when you click on the marker, a tooltip will appear above it to show more information. I am using react tooltip. Here's my index.jsx
<GoogleMaps
bootstrapURLKeys={{ key: /* KEY */ }}
center={center}
zoom={zoom}
>
<ReactTooltip
id="google"
html
multiline
/>
{markers.map((marker) => (
<Marker
key={marker.key}
place={marker}
lat={marker.lat}
lng={marker.lng}
/>
))}
</GoogleMaps>
Here's my Marker.jsx
import React from 'react';
import MarkerWindow from "./MarkerWindow"
const Marker = ({ place }) => {
const tip =
<>
<MarkerWindow place={place}/>
</>;
return (
<>
<Wrapper
data-for="google"
data-event="click focus"
data-tip={tip}
/>
</>
)
};
export default Marker;
And then my MarkerWindow.jsx
import React from 'react';
import PropTypes from 'prop-types';
import styled from 'styled-components';
const MarkerWindow = ({ place }) =>
(
<StyledSection>
<h3>${place.name}</h3>
<p>
${place.address}
<br />
${place.number}
<br />
</p>
</StyledSection>
);
const StyledSection = styled.div`
width: 18px;
height: 18px;
background-color: #e01414;
`;
MarkerWindow.propTypes = {
place: PropTypes.object.isRequired,
};
export default MarkerWindow;
But when I load the page, I get this:
How do I load custom components into ReactTooltip?

react-tooltip doesn't support components as the tooltip content, but accepts html strings.
You should use the data-html={true} prop to let the react-tooltip know that the value passed to data-tip is actually an HTML code.
Then you should provide the html of the tooltip content as a string.
So this should work:
const Marker = ({ place }) => {
const tip = `
<div style="width: 18px; height: 18px; background-color: '#e01414';" >
<h3>${place.name}</h3>
<p>
${place.address}
<br />
${place.number}
<br />
</p>
</div>
`;
return (
<>
<Wrapper
data-for="google"
data-event="click focus"
data-html={true}
data-tip={tip}
/>
</>
)
};
I converted your MarkerWindow component to an HTML string by inlining the styles from the styled-component.
However, if you are not limmited to use react-tooltip I suggest to use other packages that let you render react components for a tooltip content. Take a look at #tippyjs/react for example.

Related

'SearchIcon' is not defined no-undef

I have tried to display a search icon inside my search bar, but it isn't working. my code is
import React, { useEffect } from "react";
//31915c5e
const API_URL = 'http://www.omdbapi.com?apikey = 31915c5e'
const App = () => {
const searchMovies = async (title) => {
const response = await fetch(`${API_URL}&s=${title}`);
const data = await response.json();
console.log(data.search);
}
useEffect(() => {
searchMovies('spiderman');
}, []);
return (
<div className="app">
<h1>MovieLand</h1>
<div className="search">
<input
placeholder="search for movies"
/>
/*https://react-icons.github.io/react-icons/icons?name=gi*/
<img
src={SearchIcon}
alt="search"
/>
</div>
</div>
);
}
export default App;
showing a search icon inside my search bar.
on the browser it displays 'SearchIcon' is not defined no-undef
There are different ways of doing this, I would either install #fortawesome/react-fontawesome and #fortawesome/free-solid-svg-icons and do it with that, or, the option I would prefer that consists in using inline styles, positioning the icon as a background of the input tag; it would be something like this:
import React, { useState } from "react";
import searchIcon from "path/to/search-icon.svg";
function SearchInput() {
const [searchTerm, setSearchTerm] = useState("");
return (
<div>
<input
type="text"
placeholder="Search"
value={searchTerm}
onChange={(e) => setSearchTerm(e.target.value)}
style={{
padding: "0.5rem 0.5rem 0.5rem 2rem",
background: `url(${searchIcon}) no-repeat`,
backgroundSize: "1.5rem",
backgroundPosition: "0.5rem center",
}}
/>
</div>
);
}
export default SearchInput;
This will create an input element with a search icon as its background, and the icon will be positioned inside the input at the center left of the input field.
You can adjust the position of the icon using the background-position property and you can adjust the size of the icon using the background-size property.

Opening Google Map onClicking the Button | Reactjs

I have a map component, in that, I used Google-map-react library to display locations in google map. Inside that map, I am having a button called "View in maps". So, now, if the user clicks on that button. it should take them to the google map with given location coordinates. Please find code below and let me know, how can I achieve that
import React, { Component } from 'react';
import { withStyles } from '#material-ui/core/styles';
import styles from '../../../../../../assets/css/Me.style.js';
import GoogleMapReact from 'google-map-react';
import { Typography, Button } from '#material-ui/core';
import Marker from '#material-ui/icons/LocationOnOutlined'
class DetailsMap extends Component {
static defaultProps = {
center: { lat: 40.7446790, lng: -73.9485420 },
};
render() {
const {classes} = this.props;
return (
<div className={classes.root}>
<div className={classes.googleMap}>
<GoogleMapReact
zoom = {11}
onClick={this.onClick}
defaultCenter={ this.props.center }
>
<Marker
style={{color: '#ff7777'}}
lat={40.7473310}
lng={-73.8517440}
/>
<div className={classes.address}>
<div className={classes.addressWrapper}>
<Typography className={classes.addressHead}>
Address
</Typography>
<Typography className={classes.addressContent}>
221, raj start, doler street<br />
QC J9PB6X
</Typography>
<Button variant='outlined' className={classes.view}>
View in Maps
</Button>
</div>
</div>
</GoogleMapReact>
</div>
</div>
)
}
}
export default withStyles(styles, {withTheme:true})(DetailsMap);
I use this code for show google map in new tab with specific location with marker.
const showInMapClicked = () => {
window.open("https://maps.google.com?q="+your_lat+","+your_lng );
};
Google maps can take GET parameters for coordinates to open on_load. Make an href to this address
https://www.google.com/maps/#${your_lat},${your_lng},${your_desired_zoom}z
Note
To be able to use the formating provided in the above answer you have to use `` quotes over the normal " " or ' '

No image uploaded "SRC" is undefined even with a check?

I'm facing a weird problem here I've created a check whether an image is uploaded or not. But for some reason, my "else" is not working.
Inside my main component "Fragrances" I'm looping through my API. And checking if the Array of images is empty return else show the image.
What am I doing wrong?
My code:
image component:
import React from 'react';
import NoPicture from 'components/NoPicture/NoPicture';
const Image = props => {
const { url } = props;
return url.length > 0 ? <img src={url} className="image" /> : <NoPicture />;
};
export default Image;
NoPicture component:
import React from 'react';
// No Picture
import NoPhotoImageURL from '../../images/no-photo.svg';
console.log(NoPhotoImageURL);
const NoPicture = () => (
<img
src={NoPhotoImageURL}
alt="No Photo"
className="image image--default"
/>
);
export default NoPicture;
Main component:
import React from 'react';
import { SITE_URL } from 'constants/import';
import Name from './name';
import Category from './category';
import Image from './image';
import Gallery from 'components/Gallery/gallery';
import Rating from './rating';
import Description from './description';
import Notes from './notes';
const Fragrances = props => {
const { FragranceData } = props;
return (
<section className="fragrances">
<div className="row">
{FragranceData.map(fragrance => {
console.log(fragrance);
const {
category,
description,
image,
gallery,
name,
rating,
notes,
Publish: isPublished,
} = fragrance;
const imageURL = image.path;
return isPublished ? (
<div key={fragrance._id} className="col-xs-12 col-sm-6">
<div className="fragrance">
<Name name={name} />
<Category category={category} />
<Image url={SITE_URL + imageURL} />
<Rating rating={rating} />
<Description description={description} />
<Notes notes={notes} />
<Gallery imageData={gallery} />
</div>
</div>
) : (
'Nothing published yet!'
);
})}
</div>
</section>
);
};
export default Fragrances;
Your question is not entirely clear on what exactly you are experiencing, but here is the most obvious problem in your code. You have this line in your Image component:
return url.length > 0 ? <img src={url} className="image" /> : <NoPicture />;
However, in your main component you are passing a concatenated string to the Image component:
<Image url={SITE_URL + imageURL} />
According to your comment, SITE_URL is the full URL, which will never be empty, so inside your Image component, url will always contain something, no matter what the value of imageURL is in the main component. Thus, url.length will always be greater than 0, and the img tag will render every time.
You will either need to pass the individual parts of the path down to the Image component separately, or move your check up into the main component.

Why I get props is undefined?

import React from "react";
import styles from "../articles.css";
const TeamInfo = props => (
<div className={styles.articleTeamHeader}>
<div className={styles.left}>
style={{
background: `url('/images/teams/${props.team.logo}')`
}}
</div>
<div className={styles.right}>
<div>
<span>
{props.team.city} {props.team.name}
</span>
</div>
<div>
<strong>
W{props.team.stats[0].wins}-L{props.team.stats[0].defeats}
</strong>
</div>
</div>
</div>
);
export default TeamInfo;
the code that render this
import React from 'react';
import TeamInfo from '../../Elements/TeamInfo';
const header = (props) => {
const teaminfofunc = (team) => {
return team ? (
<TeamInfo team={team}/>
) : null
}
return (
<div>
{teaminfofunc(props.teamdata)}
</div>
)
}
export default header;
and I am getting error TypeError: props is undefined in line 8 why is that ?
Line 8 is
background: url('/images/teams/${props.team.logo}')
Update:
I found that in index.js the componentWillMount bring the data correctly but in the render() those data (article and team) was not passed to render, any idea why ?
import React, {Component} from 'react';
import axios from 'axios';
import {URL} from "../../../../config";
import styles from '../../articles.css';
import Header from './header';
import Body from './body';
class NewsArticles extends Component {
state = {
article:[],
team: []
}
componentWillMount() {
axios.get(`${URL}/articles?id=${this.props.match.params.id}`)
.then(response => {
let article = response.data[0];
axios.get(`${URL}/teams?id=${article.team}`)
.then(response => {
this.props.setState({
article,
team:response.data
})
})
})
}
render() {
const article = this.state.article;
const team = this.state.team;
return (
<div className={styles.articleWrapper}>
<Header teamdata={team[0]} date={article.date} author={article.author} />
<Body />
</div>
)
}
}
export default NewsArticles;
You render your component immediately, long before your AJAX call finishes, and pass it the first element of an empty array:
<Header teamdata={team[0]}
componentWillMount does not block rendering. In your render function, short circuit if there's no team to render.
render() {
const { article, team, } = this.state;
if(!team || !team.length) {
// You can return a loading indicator, or null here to show nothing
return (<div>loading</div>);
}
return (
<div className={styles.articleWrapper}>
<Header teamdata={team[0]} date={article.date} author={article.author} />
<Body />
</div>
)
}
You're also calling this.props.setState, which is probably erroring, and you should never call setState on a different component in React. You probably want this.setState
You should always gate any object traversal in case the component renders without the data.
{props && props.team && props.team.logo ? <div className={styles.left}>
style={{
background: `url('/images/teams/${props.team.logo}')`
}}
</div> : null}
This may not be you exact issue, but without knowing how the prop is rendered that is all we can do from this side of the code.
Update based on your edit. You can't be sure that props.teamdata exists, and therefore your component will be rendered without this data. You'll need to gate this side also, and you don't need to seperate it as a function, also. Here is an example of what it could look like:
import React from 'react';
import TeamInfo from '../../Elements/TeamInfo';
const header = (props) => (
<div>
{props.teamdata ? <TeamInfo team={props.teamdata}/> : null}
</div>
)
export default header;
First -- while this is stylistic -- it's not good practice to pass props directly to your functional component. Do this instead.
const TeamInfo = ({team}) => (
<div className={styles.articleTeamHeader}>
<div className={styles.left}>
style={{
background: `url('/images/teams/${team.logo}')`
}}
</div>
<div className={styles.right}>
<div>
<span>
{team.city} {team.name}
</span>
</div>
<div>
<strong>
W{team.stats[0].wins}-L{team.stats[0].defeats}
</strong>
</div>
</div>
</div>
);
Second, you might just want to do some kind of null check. If team is undefined the first time the component tries to render, you might just want to render null so you're not wasting cycles.
In case this isn't the issue, you'd learn a lot by console.log-ing your props so you know what everything is each time your component tries to render. It's okay if data is undefined if you're in a state that will soon resolve.

React-bootstrap styling showing up before finished api call

I am making a small application in React that fetches a random image using Axios. I am using React-bootstrap to style the image, however a small white box is displayed for half of a second before the image is done loading. How can I resolve this?
This is my code:
import React, { Component } from 'react';
import axios from 'axios';
import { Link } from 'react-router-dom';
import { Image, Button } from 'react-bootstrap';
const ROOT_URL = 'myurl'
export default class WhatDog extends Component {
constructor(props){
super(props);
this.state = { randomImg: '' };
}
componentDidMount(){
axios.get(ROOT_URL)
.then(res => {
const data = res.data.message
this.setState({ randomImg: data })
})
}
renderImage(){
return(
<div>
<Image src={this.state.randomImg} className="img" thumbnail/>
<Link to="/">
<Button bsStyle="danger" bsSize="large">Go back</Button>
</Link>
</div>
)
}
render() {
return (
<div className="App">
{
(this.state.randomImg === '')
? <div>
<h1>Loading...</h1>
</div>
: <div>
{this.renderImage()}
</div>
}
</div>
);
}
}
The browser will fire the onLoad event, after the image has been, you name it.. loaded so before that, set the visibility to hidden.. NOT display: none ! Because display: none will also prevent the loading.
The solution might look something like this
<Image
src={this.state.randomImg}
style={!this.state.imgVisible ? {visibility: 'hidden'} : {}}
onLoad={() => this.setState({ imgVisible: true })}
/>
Note: This is using inline styles and arrow functions, which is not best, but for simplicity of the demo its enough, you could also you a className instead, its up to you ;)
You got the idea...

Categories

Resources