`
import React, { useEffect, useRef, useState, useCallback } from "react";
import Button from "../../elements/buttons/Button";
import Icon from "../../elements/icons/Icon";
function useDynamicSVGImport(name, options = {}) {
const ImportedIconRef = useRef();
const [loading, setLoading] = useState(false);
const [error, setError] = useState();
const { onCompleted, onError } = options;
useEffect(() => {
setLoading(true);
const importIcon = async () => {
try {
ImportedIconRef.current = (
await import(`./${name}.svg`)
).ReactComponent;
if (onCompleted) {
onCompleted(name, ImportedIconRef.current);
}
} catch (err) {
if (onError) {
onError(err);
}
setError(err);
} finally {
setLoading(false);
}
};
importIcon();
}, [name, onCompleted, onError]);
return { error, loading, SvgIcon: ImportedIconRef.current };
}
/**
* Simple wrapper for dynamic SVG import hook. You can implement your own wrapper,
* or even use the hook directly in your components.
*/
/** const Icon = ({ name, onCompleted, onError, ...rest }) => {
const { error, loading, SvgIcon } = useDynamicSVGImport(name, {
onCompleted,
onError
});
if (error) {
return error.message;
}
if (loading) {
return "Loading...";
}
if (SvgIcon) {
return <SvgIcon {...rest} />;
}
return null;
};
*/
export default function FacialReactions() {
const [name, setName] = useState("svg1");
const handleOnCompleted = useCallback(
(iconName) => console.log(`${iconName} successfully loaded`),
[]
);
const handleIconError = useCallback((err) => console.error(err.message), []);
return (
<div className="App">
<button
onClick={() =>
setName((prevName) => (prevName === "svg1" ? "svg2" : "svg1"))
}
>
Change Icon
</button>
<section>
<Icon icon="mood-vgood" variant="horizontal" />
<Icon icon="mood-good" variant="horizontal" />
<Icon icon="mood-neutral" variant="horizontal" />
<Icon icon="mood-bad" variant="horizontal" />
<Icon icon="mood-vbad" variant="horizontal" />
/** now i had to change the entire functionality to fit the attached Picture in the head of the question! */
/**
<Icon
name={name}
fill="gray"
onCompleted={handleOnCompleted}
onError={handleIconError}
/>
<Icon
name="svg1"
fill="gray"
width="300"
onCompleted={handleOnCompleted}
onError={handleIconError}
/>
<Icon
name="svg2"
fill="darkblue"
height="100"
onCompleted={handleOnCompleted}
onError={handleIconError}
/>
*/
</section>
</div>
);
}
`
I have been struggling with implementing the functionality of the attached design, a react feedback reaction using predefined SVG facial icons . But I can not figure out from where to start.
I have already the Icon set ready to use, however, I do not know how to come up with such functionality, How to add these SVG icons into a reusable react component!. now I had to change the entire functionality to fit the attached Picture in the head of the question!
please any help with that matter will be deeply appreciated! Thanks
You could just add onClick functionality to the svg.
<path onClick={()=>setMood(1)}></path>
For adding svg to react/jsx you can take a look at this https://blog.logrocket.com/how-to-use-svgs-in-react/
Related
I'm making movie app (using react.js)
I want to show a list of new movies whenever user scrolls down.
but when i write these codes, it doesn't work.
I used react-intersection-observer and made the second useEffect for adding new list.
can you see what is the problem...?
**import { useInView } from "react-intersection-observer";**
import { useEffect, useRef, useState } from "react";
import Movie from "../components/Movie";
import HeaderComponent from "../components/HomeButton";
import GlobalStyle from "../GlobalStyle";
import { Route, useParams } from "react-router-dom";
import { LoadingStyle, ListContainer } from "../components/styles";
function Home() {
const [loading, setLoading] = useState(true);
const [movies, setMovies] = useState([]);
const [movieSearch, setMovieSearch] = useState("");
const [movieName, setMovieName] = useState("");
const [pageNumber, setPageNumber] = useState(1);
** const { ref, inView } = useInView({
threshold: 0,
});**
const param = useParams();
const getMovies = async () => {
const json = await (
await fetch(
`https://yts.mx/api/v2/list_movies.json?minimum_rating=1&page=${pageNumber}&query_term=${movieName}&sort_by=year`
)
).json();
setMovies(json.data.movies);
setLoading(false);
};
const onChange = event => {
setMovieSearch(event.target.value);
};
const onSubmit = event => {
event.preventDefault();
if (typeof param === Object) {
setMovieName(movieSearch);
getMovies();
} else {
Route(`/main`);
}
};
// When User Searching...
useEffect(() => {
getMovies();
}, [movieName]);
// When User Scroll, Keep Adding Movies at the bottom...
** useEffect(() => {
setPageNumber(prev => prev + 1);
setMovies(prev => {
return [...movies, ...prev];
});
getMovies();
}, [inView]);
**
return (
<>
<GlobalStyle />
<HeaderComponent
onSubmit={onSubmit}
onChange={onChange}
movieSearch={movieSearch}
/>
{loading ? (
<LoadingStyle>Loading...</LoadingStyle>
) : (
<>
<ListContainer>
{movies.map(item => {
return (
<Movie
key={item.title}
id={item.id}
title={item.title}
year={item.year}
medium_cover_image={item.medium_cover_image}
rating={item.rating}
runtime={item.runtime}
genres={item.genres}
summary={item.summary}
/>
);
})}
</ListContainer>
**{inView ? <>๐ญ</> : <>๐งถ</>}**
<div ref={ref} style={{ width: "100%", height: "20px" }}></div>
</>
)}
</>
);
}
export default Home;
and, when i debug this code, this errors comes out.
react_devtools_backend.js:4026 Warning: Encountered two children with the same key, `Headless Horseman`. Keys should be unique so that components maintain their identity across updates. Non-unique keys may cause children to be duplicated and/or omitted โ the behavior is unsupported and could change in a future version.
at div
at O (http://localhost:3000/static/js/bundle.js:47415:6)
at Home (http://localhost:3000/static/js/bundle.js:908:80)
at Routes (http://localhost:3000/static/js/bundle.js:41908:5)
at Router (http://localhost:3000/static/js/bundle.js:41841:15)
at BrowserRouter (http://localhost:3000/static/js/bundle.js:40650:5)
at App
I'm building a QR scanner inside a ReactJS web app that is supposed to run on both Android and iOS. However, I cannot get the torch/flashlight to work on iOS.
I'm using the #blackbox-vision toolbox to handle both the torch and the QR scanner. As far as I understand you need to start the camera functionality and can use the video stream to manipulate the torch. Below code works fine on Android but not on iOS:
import { useState, useEffect, useRef } from "react";
import { QrReader } from "#blackbox-vision/react-qr-reader";
import { useTorchLight } from "#blackbox-vision/use-torch-light";
import styles from "./view.module.css";
import IconButton from "../../components/UI/iconbutton/view";
function SAQRView() {
const streamRef = useRef(null);
const [on, toggle] = useTorchLight(streamRef.current);
const [showTorchToggleButton, setShowTorchToggleButton] = useState(false);
const [msg, setMsg] = useState("");
const setRef = ({ stream }) => {
streamRef.current = stream;
setShowTorchToggleButton(true);
};
const previewStyle = {
width: "100%",
};
const onError = (error) => {
console.log(error);
};
const onTorchClick = (event) => {
toggle();
};
return (
<>
<div className={styles.container}>
<div className={styles.sub_container}>
<QrReader
delay={100}
showViewFinder={false}
style={previewStyle}
onLoad={setRef}
onError={onError}
onScan={setData}
constraints={{
facingMode: "environment",
video: true,
}}
/>
<div className={styles.footer}>
{showTorchToggleButton && (
<IconButton
icon="Flash_off"
toggleIcon="Flash_on"
isToggled={on}
onClick={onTorchClick}
/>
)}
</div>
{msg}
</div>
</div>
</>
);
}
export default SAQRView;
So then I tried manipulating the video stream manually:
import { useState, useEffect, useRef } from "react";
import { QrReader } from "#blackbox-vision/react-qr-reader";
import { useTorchLight } from "#blackbox-vision/use-torch-light";
import styles from "./view.module.css";
import IconButton from "../../components/UI/iconbutton/view";
function SAQRView() {
const streamRef = useRef(null);
const [on, toggle] = useTorchLight(streamRef.current);
const [showTorchToggleButton, setShowTorchToggleButton] = useState(false);
const [msg, setMsg] = useState("");
const setRef = ({ stream }) => {
streamRef.current = stream;
setShowTorchToggleButton(true);
};
const previewStyle = {
width: "100%",
};
const onError = (error) => {
console.log(error);
};
const onTorchClick = (event) => {
const tracks = streamRef.current.getVideoTracks();
const track = tracks[0];
setMsg(JSON.stringify(track.getCapabilities(), null, 2));
try {
if (!track.getCapabilities().torch) {
alert("No torch available.");
}
track.applyConstraints({
advanced: [
{
torch: true,
},
],
});
} catch (error) {
alert(error);
}
};
return (
<>
<div className={styles.container}>
<div className={styles.sub_container}>
<QrReader
delay={100}
showViewFinder={false}
style={previewStyle}
onLoad={setRef}
onError={onError}
onScan={setData}
constraints={{
facingMode: "environment",
video: true,
}}
/>
<div className={styles.footer}>
{showTorchToggleButton && (
<IconButton
icon="Flash_off"
toggleIcon="Flash_on"
isToggled={on}
onClick={onTorchClick}
/>
)}
</div>
{msg}
</div>
</div>
</>
);
}
export default SAQRView;
Again, this works on Android, but not iOS. Notice that I stringify the track capabilities and print them at the bottom of the screen. For Android this looks as follows:
And for iOS, it looks like this:
So it seems that iOS cannot access the torch capability. However, the torch will be greyed out when the QR scanner is active, so it does seem to grab hold of the torch.
Also we have tried installing the Chrome web browser but this gave exactly the same result.
Can I get around this and if so, how?
I am an infant programmer and I am trying to fetch an api and style the results using React. My page works fine on the initial load and subsequent saves on VScode,but when I actually refresh the page from the browser I get the error thats posted on imageenter image description here:
Here is my code: App.js
```import React, { useEffect, useState } from 'react';
import './App.css';
import Students from './components/Students';
import styled from 'styled-components';
function App() {
const [studentInfo, setStudentInfo] = useState({});
const [searchResult, setSearchResult] = useState({});
const [searchTerm, setSearchTerm] = useState('');
useEffect(() => {
getStudents();
}, []);
useEffect(() => {
getStudents();
console.log('useEffect');
}, [searchTerm]);
const getStudents = async () => {
const url = 'https://api.hatchways.io/assessment/students';
console.log(url);
fetch(url)
.then((res) => res.json())
.then((data) => {
console.log(data);
searchTerm != ''
? setStudentInfo(filterStudents(data.students))
: setStudentInfo(data.students);
});
};
const filterStudents = (studentsArray) => {
return studentsArray.filter((info) => {
return (
info.firstName.toLowerCase().includes(searchTerm) ||
info.lastName.toLowerCase().includes(searchTerm)
);
});
};
console.log(searchTerm);
return (
<div className="App">
<Students
studentInfo={studentInfo}
setSearchTerm={setSearchTerm}
searchTerm={searchTerm}
/>
</div>
);
}
export default App;```
here is my component Students.js:
```import React, { useState } from 'react';
import styled from 'styled-components';
import GradeDetails from './GradeDetails';
const Students = ({ studentInfo, searchTerm, setSearchTerm }) => {
console.log(typeof studentInfo);
console.log(studentInfo[0]);
const [isCollapsed, setIsCollapsed] = useState(false);
const handleDetails = () => {
setIsCollapsed(!isCollapsed);
};
const average = (arr) => {
let sum = 0;
arr.map((num) => {
sum = sum + parseInt(num);
});
return sum / arr.length.toFixed(3);
};
console.log(isCollapsed);
return (
<Container>
<Input
type="text"
value={searchTerm}
placeholder="Search by name"
onChange={(e) => setSearchTerm(e.target.value.toLowerCase())}
/>
{studentInfo?.map((student) => (
<Wrapper key={student.id}>
<ImageContainer>
<Image src={student.pic}></Image>
</ImageContainer>
<ContentContainer>
<Name>
{student.firstName} {student.lastName}{' '}
</Name>
<Email>Email: {student.email}</Email>
<Company>Company: {student.company}</Company>
<Skills>Skill: {student.skill}</Skills>
<Average>Average:{average(student.grades)}%</Average>
</ContentContainer>
<ButtonContainer>
<Button onClick={handleDetails}>+</Button>
</ButtonContainer>
{isCollapsed && <GradeDetails studentInfo={studentInfo} />}
</Wrapper>
))}
</Container>
);
};```
Every time I have the error, I comment out the codes in Students.js starting from studentInfo.map until the and save and then uncomment it and save and everything works fine again.
I am hoping someone can help me make this work every time so that I don't have to sit at the edge of my seat all the time. Thank you and I apologize for the long question.
You are using an empty object as the initial state for studentInfo (the value passed to useState hook will be used as the default value - docs):
const [studentInfo, setStudentInfo] = useState({});
.map is only supported on Arrays. So this is failing when the component is rendering before the useEffect has completed and updated the value of studentInfo from an object, to an array. Try swapping your initial state to be an array instead:
const [studentInfo, setStudentInfo] = useState([]);
I am working on a News Web Application in React.js, and I am trying to figure out how to route a user to another component's page containing more information about the article they clicked. I've tried a lot of things but I'm pretty lost. I created a hook that fetches the data from a url link with its given data of a news article.
import { useState, useEffect } from 'react';
export const useHttp = (url, dependencies) => {
const [isLoading, setIsLoading] = useState(false);
const [fetchedData, setFetchedData] = useState(null);
useEffect(() => {
setIsLoading(true);
console.log('Sending Http request');
fetch(url)
.then(res => {
if (!res.ok) {
throw new Error('Failed to fetch');
}
return res.json();
})
.then(data => {
console.log(data);
setIsLoading(false);
setFetchedData(data); //something here
})
.catch(err => {
console.log(err);
setIsLoading(false);
});
// eslint-disable-next-line
}, dependencies);
return [isLoading, fetchedData];
};
I have several components, with very similar functionality, that use this hook to grab all of the articles from a url particular to that component and maps its data to get values that are later mapped to populate a card component that is called in all of these other components, so that the news articles show on the currently viewed page. Here is a component for science news.
import React from 'react';
import { Spinner, CardDeck } from 'react-bootstrap';
import { CardElement } from './Card';
import { useHttp } from '../hooks/http';
const sciUrl = 'http://newsapi.org/v2/top-headlines?country=us&category=science&apiKey=60d859683c7d4ae68160e82bcc4d39ea';
export const SciNews = () => {
const [isLoading, fetchedData] = useHttp(sciUrl, []);
// const classes = useStyles();
const sciNews = fetchedData
? fetchedData.articles.map((article, index) => ({
image: article.urlToImage,
name: article.source.name,
title: article.title,
description: article.description,
id: index,
}))
: [];
let content = <Spinner animation="grow" />;
if (!isLoading && sciNews && sciNews.length > 0) {
content = (
<div>
{sciNews.map((article) => (
<CardElement key={article.id} article={article} />
))}
</div>
);
} else if (!isLoading && (!sciNews || sciNews.length === 0)) {
content = <p>Could not fetch any data.</p>;
}
return content;
};
const sciHomeUrl = 'http://newsapi.org/v2/top-headlines?country=us&category=science&pageSize=3&sortBy=popularity&apiKey=60d859683c7d4ae68160e82bcc4d39ea';
export const SciHomeNews = () => {
const [isLoading, fetchedData] = useHttp(sciHomeUrl, []);
// const classes = useStyles();
const sciHomeNews = fetchedData
? fetchedData.articles.map((article, index) => ({
image: article.urlToImage,
name: article.source.name,
title: article.title,
description: article.description,
id: index,
}))
: [];
let content = <Spinner animation="grow" />;
if (!isLoading && sciHomeNews && sciHomeNews.length > 0) {
content = (
<CardDeck style={{paddingLeft: "5%", paddingRight: "5%"}}>
{sciHomeNews.map((article) => (
<CardElement key={article.id} article={article} />
))}
</CardDeck>
);
} else if (!isLoading && (!sciHomeNews || sciHomeNews.length === 0)) {
content = <p>Could not fetch any data.</p>;
}
return content;
};
Here is the Card Component..
import React from 'react';
import { Card, Button } from 'react-bootstrap';
export const CardElement = ({ article }) => (
<Card
style={{ height: '75vh' }}
key={article.id}
bg="dark"
text="white"
className="my-3"
>
<Card.Img style={{ height: '50%' }} variant="top" src={article.image} />
<Card.Body>
<Card.Text>{article.name}</Card.Text>
<Card.Title>{article.title}</Card.Title>
<Card.Text>{article.description}</Card.Text>
</Card.Body>
</Card>
);
How can I add a button or link to the card component to route the user to a details page, showing only the article clicked? I would like to include the actual content, author, date, etc. on this page of just that 1 article.
Thanks
I have the following code which I use to subscribe to branch.io. Here, the first console.log() is printed. But it doesn't go beyond that. What am I doing wrong here?
import React, { useEffect, useState } from 'react';
import { View } from 'react-native';
import branch from 'react-native-branch';
import {
Button,
InviteSummary,
ListItem,
ScreenContainer,
Seperator,
Label,
} from 'components';
import { APP_HOME_SCREEN } from 'common/constants/screens';
import { copyContent, shareContent } from 'library/Utils';
import ChevronRight from 'res/icons/Chevron/Right';
import Copied from 'res/icons/System/checked.svg';
import Copy from 'res/icons/System/Copy';
const SCREEN_TITLE = 'Invite and earn';
let _unsubscribeFromBranch = null;
const Invite = () => {
const [referralLink, setReferralLink] = useState('');
const [linkCopied, handleCopy] = useState(false);
useEffect(() => {
console.log('before subscribe');
_unsubscribeFromBranch = branch.subscribe(({ error, params }) => {
console.log('in subscribe');
if (error) {
console.error('Error from Branch: ', error);
return;
}
console.log('Branch params: ', JSON.stringify(params));
if (!params['+clicked_branch_link']) return;
});
}, []);
const handleCopyLink = (text: string) => copyContent(text, () => handleCopy(true));
const handleShareLink = () => shareContent(referralLink);
return (
<>
<ScreenContainer
title={SCREEN_TITLE}
backScreen={APP_HOME_SCREEN}
backScreenIcon={<ChevronRight />}>
<View>
<InviteSummary
upperMainText='S$40.00'
lowerMainText='earned'
leftSubText='7 invites earned'
rightSubText='3 pending'
/>
<Label
type={Label.Types.BODY_SMALL}
label='when you invite a friend to no matter how many friends! To make things sweeter, each friend you invite gets S$5 too.'
/>
<Label
type={Label.Types.BODY_SMALL}
label='Referral credit will be awarded to you automatically once your friend activates their Visa debit card.'
/>
<Seperator />
<Label
type={Label.Types.BODY_SMALL}
label='SHARE Your unique invite link'
/>
<ListItem
title={referralLink}
rightIcon={linkCopied ? <Copied /> : <Copy />}
disabled={linkCopied}
onPress={() => handleCopyLink(referralLink)}
/>
</View>
</ScreenContainer>
<View>
<Button
type={Button.Types.PRIMARY}
text='SHARE LINK'
disabled={!linkCopied}
onPress={handleShareLink}
/>
</View>
</>
);
};
export default Invite;