I've created a section on a site that is two sliders, the first slider is visible and on click the slide will display the corresponding slide beneath with the bottom slider initially starting as a display: none element. Each top slide also has a button on it that will anchor you down to the bottom slider to put it in a better view position.
If you navigate from the homepage onto this page and click on the anchor button first to display the content, the page does anchor down slightly but the content does not display, it also pastes the anchor link into the URL which doesn't seem to be how the working version behaves. On load the page is getting an error in the console saying Can't perform a React state update on an unmounted component.
You then have to click the object again or reload the page to get the anchor function/content to display as expected. Reloading the page also removes the React state console error as I'm assuming the component does mount.
The slider is built using Slick React slider and I'm using Gatsby Anchor Link Plugin and React Animate Height to perform both the hide/show height and anchor functions as I learnt in the project you can't just add a class to add height or just anchor stuff in React which is fun/backwards.
The component class for the page and the 2 components are below:
class AboutPage extends React.Component {
_isMounted = false;
constructor(props) {
super(props)
this.heroTextRef = React.createRef()
this.teamRef = React.createRef()
this.serviceGroupsRef = React.createRef()
this.state = {
nav1: null,
nav2: null,
isShow: false,
height: 0,
activeColor: false
}
this.openHeight = this.openHeight.bind(this);
this.closeHeight = this.closeHeight.bind(this);
}
openHeight = () => {
const { height } = this.state;
const { activeColor } = this.state;
this.setState({
height: 'auto',
activeColor: true,
});
};
closeHeight = () => {
const { height } = this.state;
const { activeColor } = this.state;
this.setState({
height: 0,
activeColor: false,
});
};
componentDidMount() {
this.setState({
nav1: this.slider1,
nav2: this.slider2
});
this._isMounted = false;
}
render() {
const { height } = this.state;
const team = get(this, 'props.data.contentfulPageContentStudio.theOrcaTeam')
return (
<Layout
location={this.props.location}
inversionRefs={[this.serviceGroupsRef, this.teamRef]}
>
<SEO
siteTitle={'About Us - ' + siteTitle}
/>
<TeamWrapper ref={this.teamRef} ref={this.serviceGroupsRef}>
<Container width={14}>
<Reveal
fraction={ANIMATION.revealFriction}
keyframes={customReveal}
triggerOnce
>
<TeamGridTitle id="topSlider">The Orca Team</TeamGridTitle>
</Reveal>
<Slider
dots={false}
infinite={false}
speed={500}
slidesToShow={3}
slidesToScroll={1}
swipeToSlide={true}
arrows={false}
asNavFor={this.state.nav2}
focusOnSelect={true}
responsive={[
{
breakpoint: 1024,
settings: {
slidesToShow: 2
}
}
]}
ref={slider => (this.slider1 = slider)}
className="top-slider"
>
{team.map((person, i) => (
<>
<TeamCard key={slugify(person.firstName)} index={i} person={person} onClick={this.openHeight} colour={this.state.activeColor}/>
</>
))}
</Slider>
</Container>
<AnchorTag id="bottomSlider" />
<AnimateHeight
duration={ 500 }
height={ height }
>
<SliderContainer>
<Slider
asNavFor={this.state.nav1}
ref={slider => (this.slider2 = slider)}
slidesToShow={1}
fade={true}
adaptiveHeight={true}
arrows={false}
dots={false}
infinite={false}
>
{team.map((person, i) => (
<>
<PersonBio key={slugify(person.lastName)} index={i} person={person} onClick={this.closeHeight}/>
</>
))}
</Slider>
</SliderContainer>
</AnimateHeight>
</TeamWrapper>
</Layout>
)
}
}
Top Slider Component:
const TeamCard = (props) => {
return (
<Card
onClick={props.onClick}
className={props.colour ? 'team-card' : null}
>
{props.person.image && <Img fluid={props.person.image.fluid} />}
<Info image={props.person.image} className="card-item">
<Name>
{props.person.firstName} {props.person.lastName}
</Name>
<Role className="small">{props.person.role}</Role>
<AnchorLink
to="/about/#bottomSlider"
title="Top Link"
>
<DownArrow version="1.1" x="0px" y="0px" viewBox="0 0 64 64" width="24" height="24">
<polyline
fill="none"
stroke="#FFFFFF"
strokeWidth="8"
strokeMiterlimit="10"
points="3.3,17.2 32,46.8 60.7,17.2 "/>
</DownArrow>
</AnchorLink>
</Info>
</Card>
)
}
Bottom Slider Component:
const PersonBio = (props) => {
return (
<Card className="card-block">
<Info>
<Container width={14}>
<FlexRow>
<PersonInfo>
<Name>
{props.person.firstName} {props.person.lastName}
</Name>
<Role className="small">{props.person.role}</Role>
<Meta>
{props.person.dribble && (
<IconLink
href={props.person.dribble}
target="_blank"
rel="noreferrer"
>
<IconDribbleSVG
alt={`${props.person.firstName} ${props.person.lastName} Dribbble link`}
/>
</IconLink>
)}
{props.person.instagram && (
<IconLink
href={props.person.instagram}
target="_blank"
rel="noreferrer"
>
<IconInstagramSVG
alt={`${props.person.firstName} ${props.person.lastName} Instagram link`}
/>
</IconLink>
)}
{props.person.linkedIn && (
<IconLink
href={props.person.linkedIn}
target="_blank"
rel="noreferrer"
>
<IconLinkedInSVG
alt={`${props.person.firstName} ${props.person.lastName} LinkedIn link`}
/>
</IconLink>
)}
</Meta>
</PersonInfo>
<PersonContent className="bio-text" dangerouslySetInnerHTML={{ __html: props.person.bio.bio }}>
</PersonContent>
</FlexRow>
<SvgRow>
<AnchorLink
to="/about/#topSlider"
title="Bottom Link"
>
<CloseBio x="0px" y="0px" viewBox="0 0 64 64" className="bio-close" onClick={props.onClick}>
<circle cx="32" cy="32" r="29.8"/>
<line x1="12.7" y1="32" x2="51.3" y2="32" className="crossLine1"/>
<line x1="12.7" y1="32" x2="51.3" y2="32" className="crossLine2"/>
</CloseBio>
</AnchorLink>
</SvgRow>
</Container>
</Info>
</Card>
)
}
I have a MenuComponent.js component which brings me data from an API and draws the products through a map.
import ProductCard from './Cards/ProductCard'
import {Container, Row, Col, Button} from 'react-bootstrap'
import '../styles/MenuComponent.css'
const MenuComponent = () => {
const [cart, setCart] = useState([])
const addToCart = (product) =>{
setCart([...cart, product])
}
const [productos, setProductos] = useState([])
useEffect(() => {
getProducts();
localStorage.setItem('cart', JSON.stringify(cart));
},[cart])
const getProducts = async() => {
try {
const res = await fetch('https://my-json-server.typicode.com/Fawkes11/FAKE_API_services/menu');
const data = await res.json();
setProductos(data);
} catch (error) {
console.log(error);
}
}
return (
<Container fluid className="menuComponentContainer p-5">
<Row style={{paddingLeft: '150px', paddingRight: '150px'}}>
<Col style={{display: 'flex', justifyContent:'center'}}>
<h2>Menú</h2>
</Col>
<Col style={{display: 'flex', justifyContent:'center'}}>
<Button className='productCardButton'>
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" className="bi bi-download me-2" viewBox="0 0 16 16">
<path d="M.5 9.9a.5.5 0 0 1 .5.5v2.5a1 1 0 0 0 1 1h12a1 1 0 0 0 1-1v-2.5a.5.5 0 0 1 1 0v2.5a2 2 0 0 1-2 2H2a2 2 0 0 1-2-2v-2.5a.5.5 0 0 1 .5-.5z"/>
<path d="M7.646 11.854a.5.5 0 0 0 .708 0l3-3a.5.5 0 0 0-.708-.708L8.5 10.293V1.5a.5.5 0 0 0-1 0v8.793L5.354 8.146a.5.5 0 1 0-.708.708l3 3z"/>
</svg>
Descargar
</Button>
</Col>
</Row>
<Row style={{marginTop: '20px'}}>
{
productos.map((pro) => {
return(
<Col className='cElementsMenu' key={pro.id}>
{cart.length}
<ProductCard
addToCart={addToCart}
id={pro.id}
photo={pro.imagen}
name={pro.product}
type={pro.type}
size={pro.tamaño}
price={pro.precio}
/>
</Col>
)
})
}
</Row>
</Container>
)
}
export default MenuComponent;
Next the ProductCard.jsx component which is the structure of each product.
import {Card, Button} from 'react-bootstrap'
import '../../styles/productCard.css'
const ProductCard = (props) => {
const [hover, setHover] = useState(false);
return (
<Card className='productCard' style={{ width: '18rem', borderRadius: '20px' }}>
<div
className='productOverflow'
onMouseEnter={() => setHover(true)}
onMouseLeave={() => setHover(false)}
>
{hover && <Button className='buttonPreviewProduct'>Vista previa</Button>}
<Card.Img
className={`productCardImg ${hover && 'hover'}`}
variant="top"
src={props.photo}
/>
</div>
<Card.Body className='productCardBody'>
<Card.Title className='productCardtitle'>{props.name} {props.type}</Card.Title>
<Card.Text>$ {props.price}</Card.Text>
<Button className='productCardButton' onClick={() => props.addToCart(props)}>Agregar al carrito</Button>
</Card.Body>
</Card>
)
}
export default ProductCard;
now the problem, when adding to the cart it works fine and it even reflects the amount of products added, but when adding them to the LocalStorage and then trying to read them from the next component it does not render automatically, I have to update the page (F5) to make it look reflected my product account.
CartCounterIcon.jsx
import React, { useEffect } from 'react'
import { getCartItems } from '../../modules/cart'
function CartCounterIcon(props) {
return (
<div style={countIcon.div}><p style={countIcon.p}>{getCartItems().length}</p></div>
)
}
const countIcon = {
div: {
margin: '0',
padding: '0px 6px',
position: 'absolute',
right: '0',
top: '0',
backgroundColor: '#91491a',
borderRadius: '20%',
},
p: {
margin: '0',
color: '#eadac0',
fontWeight: 'bold',
}
}
export default CartCounterIcon
the following is the function that I import to get the data from localStorage. the problem is not saving them, the problem is that when reading the local data they do not render the CartCounterIcon.jsx component beforehand thanks for your help
cart.js
export function getCartItems(){
const data = localStorage.getItem("cart");
if(!data) return [];
return JSON.parse(data);
}
I have a component like this that accepts array of object as props. the info array has key called icon which value is another component(SVG) Home, People, Bath, Size
const info = [
{
value: 1,
icon: Home
},
{
value: 2,
icon: People
},
{
value: 10,
icon: Size
},
{
value: 1,
icon: Bath
},
]
<Info info={info} />
inside the Info component:
info.map((item, i) => (
<>
<li className="inline-flex">
{item.icon}
</li>
</>
))
Now the problem is that I can't render the icon component, when I log it, it prints a function()
what should i do in order to print the icon component ?
Update:
this is how my svg Component looks like.
import React, { StatelessComponent, SVGAttributes } from "react";
export interface SvgrComponent extends StatelessComponent<SVGAttributes<SVGElement>> {}
export const People: SvgrComponent = () => {
return (
<svg
xmlns="http://www.w3.org/2000/svg"
width="24"
height="24"
viewBox="0 0 24 24"
focusable="false"
>
<path
d="M21,15V13a1,1,0,0,0-1-1H19V6a3,3,0,0,0-6,0v.5a.5.5,0,0,0,1,0V6a2,2,0,0,1,4,0v6H4a1,1,0,0,0-1,1v2a5,5,0,0,0,2.17,4.12l-1,1a.48.48,0,0,0,0,.7.48.48,0,0,0,.7,0l1.24-1.23A5,5,0,0,0,8,20h8a5,5,0,0,0,1.91-.38l1.24,1.23a.49.49,0,0,0,.7-.7l-1-1A5,5,0,0,0,21,15Zm-2.9,3.39a3.74,3.74,0,0,1-1,.43A3.8,3.8,0,0,1,16,19H8a3.8,3.8,0,0,1-1.12-.18,3.74,3.74,0,0,1-1-.43A4,4,0,0,1,4,15V13H20v2A4,4,0,0,1,18.1,18.39Z"
fill="currentColor"
/>
</svg>
);
};
Try the following:
info.map((item, i) => (
<>
<li className="inline-flex">
{item.icon()}
</li>
</>
))
svg should set as source to the img
{
info.map((item, i) => (
<>
<li className="inline-flex">
<img src={item.icon} />
</li>
</>
))}
I'd want to see more code posted since the following works:
import React from "react";
import "./style.css";
const Home = (
<svg class="svg-icon" viewBox="0 0 20 20">
<path d="M17.684,7.925l-5.131-0.67L10.329,2.57c-0.131-0.275-0.527-0.275-0.658,0L7.447,7.255l-5.131,0.67C2.014,7.964,1.892,8.333,2.113,8.54l3.76,3.568L4.924,17.21c-0.056,0.297,0.261,0.525,0.533,0.379L10,15.109l4.543,2.479c0.273,0.153,0.587-0.089,0.533-0.379l-0.949-5.103l3.76-3.568C18.108,8.333,17.986,7.964,17.684,7.925 M13.481,11.723c-0.089,0.083-0.129,0.205-0.105,0.324l0.848,4.547l-4.047-2.208c-0.055-0.03-0.116-0.045-0.176-0.045s-0.122,0.015-0.176,0.045l-4.047,2.208l0.847-4.547c0.023-0.119-0.016-0.241-0.105-0.324L3.162,8.54L7.74,7.941c0.124-0.016,0.229-0.093,0.282-0.203L10,3.568l1.978,4.17c0.053,0.11,0.158,0.187,0.282,0.203l4.578,0.598L13.481,11.723z" />
</svg>
);
const People = (
<svg class="svg-icon" viewBox="0 0 20 20">
<path d="M17.684,7.925l-5.131-0.67L10.329,2.57c-0.131-0.275-0.527-0.275-0.658,0L7.447,7.255l-5.131,0.67C2.014,7.964,1.892,8.333,2.113,8.54l3.76,3.568L4.924,17.21c-0.056,0.297,0.261,0.525,0.533,0.379L10,15.109l4.543,2.479c0.273,0.153,0.587-0.089,0.533-0.379l-0.949-5.103l3.76-3.568C18.108,8.333,17.986,7.964,17.684,7.925 M13.481,11.723c-0.089,0.083-0.129,0.205-0.105,0.324l0.848,4.547l-4.047-2.208c-0.055-0.03-0.116-0.045-0.176-0.045s-0.122,0.015-0.176,0.045l-4.047,2.208l0.847-4.547c0.023-0.119-0.016-0.241-0.105-0.324L3.162,8.54L7.74,7.941c0.124-0.016,0.229-0.093,0.282-0.203L10,3.568l1.978,4.17c0.053,0.11,0.158,0.187,0.282,0.203l4.578,0.598L13.481,11.723z" />
</svg>
);
const info = [
{
value: 1,
icon: Home
},
{
value: 2,
icon: People
}
];
const Info = ({ info }) => (
<div>
{info.map((item, i) => (
<>
<li className="inline-flex">{item.icon}</li>
</>
))}
</div>
);
export default function App() {
return <Info info={info} />;
}
I would recommend embedding in an image. this is a simple code.
{
info.map((item, i) => (
<>
<li className="inline-flex">
<img src={item.icon} />
</li>
</>
))}
I am using an application that displays a Google map with react Google Maps have multiple pins installed, and the state changes by scrolling, and the active flight changes according to the state.
At that time, the center of the Google map is set to be an activity, but the Google map is re-rendered when the state changes. I don't know how to prevent rendering.
Google Maps has NPM library. It uses react-google-maps and is implemented using hooks. I tried to return false with useEffect (), but I didn't hear it as it is. Please tell me
MapComponent(HOC)
import React from "react";
import { withGoogleMap, GoogleMap, withScriptjs, Marker, InfoWindow } from "react-google-maps";
import { compose, withProps, withHandlers, withStateHandlers } from "recompose";
const MapWithPlaces = compose(
withProps({
googleMapURL:
`https://maps.googleapis.com/maps/api/js?key=${process.env.REACT_APP_GOOGLE_PLACE_API_KEY}&libraries=geometry,drawing,places`,
loadingElement: <div style={{ height: `100%` }} />,
containerElement: <div style={{ height: "400px", width: "100%" }} />,
mapElement: <div style={{ height: "100%" }} />
}),
withStateHandlers(
props => ({
infoWindows: props.places.map(p => {
return { isOpen: false };
}),
defaultCenter: { 'lat': props.lat, 'lng': props.lng }
}),
{
onToggleOpen: ({ infoWindows }) => selectedIndex => ({
infoWindows: infoWindows.map((iw, i) => {
iw.isOpen = selectedIndex === i;
return iw;
})
})
}
),
withHandlers(() => {
const refs = {
map: undefined,
}
console.log(refs);
return {
onMapMounted: () => ref => {
refs.map = ref
},
onZoomChanged: ({ onZoomChange }) => (props) => {
const center = { 'lat': parseFloat(props.lat, 10), 'lng': parseFloat(props.lng, 10) }
refs.map.pantTo(center)
}
}
}),
withScriptjs,
withGoogleMap
)(props => (
<GoogleMap defaultZoom={props.zoom} defaultCenter={props.center} key={props.key} ref={map}>
{props.places &&
props.places.map((place, i) => {
let lat = parseFloat(place.lat, 10);
let lng = parseFloat(place.lng, 10);
return (
<Marker
id={place.id}
key={place.key}
position={{ lat: lat, lng: lng }}
title={place.name}
onClick={props.onToggleOpen.bind(this, i)}
opacity={place.key === props.step ? 1 : 0.5}
label={place.day === props.currentDay ? place.dayIndex.toString() : ''}
>
{props.infoWindows[i].isOpen && (
<InfoWindow onCloseClick={props.onToggleOpen.bind(i)}>
<div>{place.name}</div>
</InfoWindow>
)}
</Marker>
);
})}
</GoogleMap>
));
export default MapWithPlaces;
MapComponent(hooks)
import React, { useState, useEffect, useRef } from "react";
import { withGoogleMap, withScriptjs, GoogleMap, Marker, InfoWindow } from "react-google-maps";
// import mapStyles from "./mapStyles";
const MapCreate = React.memo((props) => {
// const [selectedPark, setSelectedPark] = useState(null);
const mapRef = useRef()
useEffect(() => {
console.log("props updates")
console.log(props);
const mapCenter = {
lat: parseFloat(props.places[props.step].lat, 10),
lng: parseFloat(props.places[props.step].lng, 10)
}
return false
// refMap.current.panTo(mapCenter) //move the map to new location
}, [props]);
return (
<GoogleMap defaultZoom={14} center={{ lat: props.center.lat, lng: props.center.lng }} ref={mapRef}>
{props.places && props.places.map((place, i) => {
let lat = parseFloat(place.lat, 10);
let lng = parseFloat(place.lng, 10);
return (
<Marker
id={place.id}
key={place.key}
position={{ lat: lat, lng: lng }}
title={place.name}
opacity={place.key === props.step ? 1 : 0.5}
label={place.day === props.currentDay ? place.dayIndex.toString() : ''}
>
</Marker>
)
})}
</GoogleMap>
)
})
const MapWrapped = withScriptjs(withGoogleMap(MapCreate));
export default function Map(props) {
const mapRef = useRef(null)
return (
<div style={{ width: "100%", height: "400px" }}>
<MapWrapped
googleMapURL={`https://maps.googleapis.com/maps/api/js?v=3.exp&libraries=geometry,drawing,places&key=${process.env.REACT_APP_GOOGLE_PLACE_API_KEY}`}
loadingElement={<div style={{ height: `100%` }} />}
containerElement={<div style={{ height: "400px", width: "100%" }} />}
mapElement={<div style={{ height: `100%` }} />}
{...props}
/>
</div>
);
}
Try #shouldcomponentupdate
shouldComponentUpdate(nextProps, nextState)
Use shouldComponentUpdate() to let React know if a component’s output
is not affected by the current change in state or props. The default
behavior is to re-render on every state change, and in the vast
majority of cases you should rely on the default behavior.
shouldComponentUpdate() is invoked before rendering when new props or
state are being received. Defaults to true. This method is not called
for the initial render or when forceUpdate() is used