Can't render Svg Component in React - javascript

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

Related

Changing rendered 3d model based on state ReactJS

I have 3 .GLB files which I want to change based on the state (Sort of a gallery and one main image), but it loads weirdly when changing and it doesn't render some 3d models. Using three.js, ChakraUI, and nextJS. As I've only begun to test out 3d model I don't know if this is the correct way to re-render or conditionally show the model.
Series.js
const itemData = [
{
image: "/images/ssr-img1.jpg",
model: "zombie.glb",
},
{
image: "/images/ssr-img2.jpg",
model: "star-icon.glb",
},
{
image: "/images/ssr-img3.jpg",
model: "dog.glb",
},
{
image: "/images/ssr-img4.jpg",
model: "zombie.glb",
},
{
image: "/images/ssr-img5.jpg",
model: "star-icon.glb",
},
{
image: "/images/ssr-img6.jpg",
model: "dog.glb",
},
];
export default function Series() {
const [items, setItems] = useState(itemData);
const [model, setModel] = useState("star-icon.glb");
const [currentImage, setCurrentImage] = useState(itemData[0].image);
return (
<Box id="nfc" bg="#edf3f8" py="2rem">
<Suspense fallback={null}>
<Test model={model} />
</Suspense>
<Title color="black">Rarity List</Title>
<Stack>
<HStack align="center" justify="center" my="1rem" maxW="full">
{items.map((item, index) => (
<Image
maxW="5%"
key={index}
onClick={() => setModel(item.model)}
cursor="pointer"
src={item.image}
alt="test"
/>
))}
</HStack>
</Stack>
</Box>
);
}
Test.js (The 3d model)
import { Canvas, useFrame } from "#react-three/fiber";
import { useGLTF, Environment, OrbitControls } from "#react-three/drei";
import { Box } from "#chakra-ui/react";
function Dog(props) {
const { scene } = useGLTF(props.model);
return <primitive object={scene} {...props} />;
}
function Zoom() {
useFrame((state, delta) => {
state.camera.zoom += delta / 100;
state.camera.updateProjectionMatrix();
});
}
export default function Test({ model }) {
return (
<Box h="xl">
<Canvas camera={{ position: [2.5, 2.5, -2.5], fov: 35 }}>
<ambientLight intensity={0.5} />
<Dog
model={model}
position={[-0.1, -0.2, 0]}
rotation={[0, Math.PI / 2, 0]}
scale={0.2}
/>
<Environment preset="city" />
<OrbitControls
minPolarAngle={Math.PI / 2.5}
maxPolarAngle={Math.PI / 2.5}
/>
<Zoom />
</Canvas>
</Box>
);
}

React Anchor Link on Hidden Item - Can't perform a React state update on an unmounted component

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

How do i grab the document name from firebase

I have using this component to push data out, however i can not grab the document name i.e. "sampleCloudFunction" under CloudFunctionMonitor(please see picture) from the database and display it
docRef.id does not work, i am not looking for the ID but the actual name of the sub document?
<Card.Content>
<Card.Header>{this.props.cloudFunction.docRef.id}</Card.Header>
<Card.Description>{}</Card.Description>
</Card.Content>
Full code index.js:
import "../../App";
import "semantic-ui-css/semantic.min.css";
import { Icon, Card, Button } from "semantic-ui-react";
import "semantic-ui-css/semantic.min.css";
import React, { Component } from "react";
import "semantic-ui-css/semantic.min.css";
export default class Cards extends Component {
render() {
return (
<Card color="green">
<Card.Content>
<Card.Header>{this.props.cloudFunction.docRef.id}</Card.Header>
<Card.Description>{}</Card.Description>
</Card.Content>
<Card.Content extra>
<Icon name="cog" /> Records: {this.props.cloudFunction.lastRunLoad}
<br />
<Icon name="cog" />
{this.props.cloudFunction.lastRunMessage}
<br />
<Icon name="clock" /> Last Run:{" "}
{this.props.cloudFunction.lastRunTime.toDate().toString()}
<br />
<Icon name="angle double down" />
Schedule: {this.props.cloudFunction.schedule}
</Card.Content>
<Card.Content extra>
<div className="ui two buttons">
<Button basic color="green">
Cloud Function
</Button>
</div>
</Card.Content>
</Card>
);
}
}
Firestore Query
useEffect(() => {
const unsubscribe = FirestoreService.getCloudFunctionItems(
(querySnapshot) => {
const updatedCloundFunctions = querySnapshot.docs.map(
(docSnapshot) => ({
guid: uuidV4(),
...docSnapshot.data(),
})
);
console.log(updatedCloundFunctions);
setCloudFunctions(updatedCloundFunctions);
},
(error) => setError("list-item-get-fail")
);
return unsubscribe;
}, []);
The document data does not contain it's ID. You'll have to explicitly add it as shown below:
const updatedCloundFunctions = querySnapshot.docs.map((docSnapshot) => ({
guid: uuidV4(),
// Add Document ID
id: docSnapshot.id
...docSnapshot.data(),
})
);
Then you can access it by this.props.cloudFunction.id

reading data from localStorage does not render in react js

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

multiple mapbox react markers saved in state

I'm trying to add multiple markers to my map at once, but every time I add a new marker it replaces the previously placed one. I need the markers to be saved in the state so that they can all be submitted in one POST request. I have the API and Database set up, I just can't figure out this aspect of the UI.
The markers I am referring to are under the comment {/* USER ADDED MARKER */}
Thanks in advance for any help, I have been stuck on this for days.
import React, { useState, useEffect } from "react";
import ReactMapGL, { Marker, Popup } from "react-map-gl";
import { listLogEntries } from "./API";
import LogEntryForm from "./LogEntryForm";
const BasicMap = () => {
const [logEntries, setLogEntries] = useState([]);
const [showPopup, setShowPopup] = useState({});
const [showUserPopup, setShowUserPopup] = useState({});
const [addEntryLocation, setAddEntryLocation] = useState();
// viewport is all the infomation about the default map state
const [viewport, setViewport] = useState({
width: "100vw",
height: "100vh",
latitude: 52.950001,
longitude: -1.15,
zoom: 6
});
const getEntries = async () => {
const logEntries = await listLogEntries();
setLogEntries(logEntries);
};
useEffect(() => {
getEntries();
}, []);
const showAddMarkerPopup = event => {
const [longitude, latitude] = event.lngLat;
setAddEntryLocation({
latitude,
longitude
});
};
return (
<ReactMapGL
{...viewport}
mapStyle="mapbox://styles/hidden/ck7xdy1yo1bkj1ipmsbhe9c96"
mapboxApiAccessToken={process.env.REACT_APP_MAPBOX_TOKEN}
onViewportChange={setViewport}
onClick={showAddMarkerPopup}
>
{/* USER ADDED MARKER */}
{addEntryLocation ? (
<>
<Marker
latitude={addEntryLocation.latitude}
longitude={addEntryLocation.longitude}
draggable
onDragEnd={showAddMarkerPopup}
onClick={() => setShowUserPopup(true)}
>
>
<div>
<svg
className="marker red"
style={{
height: `${6 * viewport.zoom}px`,
width: `${6 * viewport.zoom}px`
}}
version="1.1"
id="Layer_1"
x="0px"
y="0px"
viewBox="0 0 512 512"
>
<g>
<g>
<path
d="M256,0C153.755,0,70.573,83.182,70.573,185.426c0,126.888,165.939,313.167,173.004,321.035
c6.636,7.391,18.222,7.378,24.846,0c7.065-7.868,173.004-194.147,173.004-321.035C441.425,83.182,358.244,0,256,0z M256,278.719
c-51.442,0-93.292-41.851-93.292-93.293S204.559,92.134,256,92.134s93.291,41.851,93.291,93.293S307.441,278.719,256,278.719z"
/>
</g>
</g>
</svg>
</div>
</Marker>
{/* This Popup should have its own ? statement ' is clicked ' */}
<Popup
latitude={addEntryLocation.latitude}
longitude={addEntryLocation.longitude}
closeButton={true}
closeOnClick={true}
dynamicPosition={true}
// onClose={() => setShowPopup({})}
onClose={() => setAddEntryLocation(false)}
anchor="top"
>
<div className="popup">
<LogEntryForm
closeOnClick={true}
onClose={() => {
// setAddEntryLocation(null);
// getEntries();
setShowUserPopup(false);
}}
location={addEntryLocation}
/>
</div>
</Popup>
</>
) : null}
</ReactMapGL>
);
};
export default BasicMap;

Categories