Related
I am using an MUI Autocomplete to act as an employee search field. A user will start entering the name of an employee which queries an API request on each keystroke. Clicking on an employee name from the search results throws the following error message The 'getOptionLabel' method of Autocomplete returned object ([object Object]) instead of a string for {"Badge":"1234567","Name":"Doe, John[John.Doe#gmail.com]"}. I want to display the employee name in the field after clicking. After clicking on a name, I want to send the Badge number to my on change function as event (e). I need to send the badge as two employees may have the same name, but will always have unique badge numbers.
const { onChange, badge, taxonomyid, label, id, disabled, style, status, ...other } = props;
const [employees, setemployees] = useState([]);
useEffect(() => {
const fetchEmployees = async () => {
var url = `/api/employeesearch?taxonomyid=${taxonomyid}`;
if (badge && +badge) {
url = url + `&badge=${badge}`;
const resp = await fetch(url);
const emp = await resp.json();
setemployees(emp);
}
};
fetchEmployees();
}, [badge, taxonomyid]);
const onNameChange = async (e) => {
e.preventDefault();
var value = e.target.value;
var url = `/api/employeesearch?taxonomyid=${taxonomyid}`;
if (value) {
url = url + `&name=${value}`;
const resp = await fetch(url);
const emp = await resp.json();
if (resp.ok) {
setemployees(emp);
} else {
return null;
}
console.log(value);
}
};
const onBadgeChange = (e) => {
e.preventDefault();
console.log(e.target.value);
console.log(e.target.name);
onChange(e);
};
...
<Autocomplete
id="search-select"
// value={employees?.employees?.Name}
// inputValue={badge} //JSON.stringify(option.Name)
label={badge}
options={employees.employees}
getOptionLabel={(option, i) => (
<div key={i} onClick={(e) => /* onBadgeChange(e) */ setVal(option.Badge)} value={option.Name.toString()}>
{option.Name}
</div>
)}
onInputChange={(e) => onNameChange(e)}
onChange={onBadgeChange}
filterOptions={(x) => x}
variant="outlined"
size="small"
name={id || "Person"}
InputProps={{
classes: {
input: classes.fields,
},
}}
renderInput={(params) => <TextField {...params} label={badge} variant="outlined" />}
/>
Here is a quick example of my employee data:
{
"employees": [
{
"Badge": "1234567",
"Name": "Doe, John[John.Doe#gmail.com]"
}
]
}
i'm using react native to make an app connected to firebase
I need to get the name of the user profile picture to display the correct one in my app
The pictures are stored on firebase storage
My code here doesn't work to get the picture download url from the name in my database
with the error "rendered more hooks than during the previous render"
function HomeScreen({ navigation }) {
const [allUsers, setAllUsers] = useState([]);
useEffect(() => {
const fechedUsers = [];
const fetchData = async () => {
try {
const querySnapshot = await getDocs(collection(db, 'users'));
let allDocs = [];
querySnapshot.forEach((doc) => {
allDocs.push({ ...doc.data(), id: doc.id });
});
for (const item of allDocs) {
fechedUsers.push(item);
}
} catch (err) {
console.log(err);
}
setAllUsers(fechedUsers);
};
fetchData();
}, []);
allUsers.sort((a, b) => (a.lastname > b.lastname) ? 1 : -1);
let cardsList = allUsers.map((user) => {
const [url, seturl] = useState([]);
useEffect(() =>{
const func = async () => {
try {
if(users.profilImg === "none"){
link = "/pdp/none.png";
}
else{
link = "/pdp/" + users.profilImg;
}
const storage = getStorage();
const reference = ref(storage, link);
await getDownloadURL(reference).then((x) =>{
seturl(x);
})
}
catch (err) {
console.log(err);
}
}
func()
}, []);
let imagePathStar;
if(user.rating >= 4.5){
imagePathStar = require('./assets/rate/full_star.png');
}
else if (user.rating >= 2.5){
imagePathStar = require('./assets/rate/half_star.png');
}
else {
imagePathStar = require('./assets/rate/empty_star.png');
}
var talent = [];
if(user.talentType == 'Nouveau Talent'){
talent = [];
}
else if(user.talentType == 'Futur Talent'){
talent = [""];
}
else if(user.talentType == 'Super Talent'){
talent = ["", ""];
}
else if(user.talentType == 'Pépite'){
talent = ["", "", ""];
}
let returned = (
<TouchableOpacity key={user.id} onPress={() => navigation.navigate('Profil', users.name)}>
<Card style={styles.cardcontainer} pointerEvents="none">
<CardImage style={styles.cardimage}
source={{uri: url}}
/>
<CardTitle
title={user.fullname}
subtitle={user.currentPosition}
/>
<CardAction
separator={false}
inColumn={false}
style={styles.cardnotes}>
<Text style={styles.cardtext}>{user.rating} <Image source={imagePathStar} style={styles.star}/> ({user.ratingCount} avis)</Text>
<Text style={styles.cardtext}>{user.talentType}
{talent.map((value, index) => {
return (
<Image source={require('./assets/Badge_talent.png')} style={styles.badge} />
);
})}
</Text>
</CardAction>
<CardAction
separator={false}
inColumn={false}
style={styles.cardtalents}>
<Text>none for now</Text>
</CardAction>
</Card>
</TouchableOpacity>
)
return(returned);
})
return (
<View style={styles.container}>
<ScrollView style={styles.scrollviewcontainer}>
{cardsList}
</ScrollView>
<StatusBar style="auto" />
</View>
);
}
can someone help me ?
hi guys im having trouble bringing back the sum of an array from a json api.
im able to select the json data and select the grades only. my plan was ... once i have the grades to use the reduce method to get the sum of each array and then divide by the length. so as you can see it didnt work out , infact it brings me some strange number which looks like the total of all the numbers in each array divided by the length of each array. i tried every single way to do it for loop etc . its not the for loop or the method im using that doesnt work, its the way im bringing down the array or atleast the way my reduce method is calculating the arrays. so how do i get the sum of each array individually? theres 25 arrays so ill need 25 sums. i placed the code down here below. or above ...
import React, { useEffect, useState } from "react";
import Card from "#material-ui/core/Card";
import CardContent from '#material-ui/core/CardContent';
import Grid from "#material-ui/core/Grid";
import { Input } from "#material-ui/core";
function StudentProfiles() {
const [info, setInfo] = useState();
const [input, setInput] = useState('');
const [filtered, setFiltered] = useState([]);
const [Average, setAverage] = useState([]);
useEffect(() => {
fetch("https://api.hatchways.io/assessment/students")
.then(response => response.json())
.then(json => setInfo(json.students))
},[]);
const filterChange = (e) => {
e.preventDefault();
const target = e.target.value;
setInput(target);
if(input !== '') {
const result = info && info.filter((user) => {
return user.firstName.toLowerCase().startsWith(input.toLowerCase());
});
setFiltered(result);
} if(input !== '') {
const result = info && info.filter((person) => {
return person.lastName.toLowerCase().startsWith(input.toLowerCase());
});
setFiltered(result);
} else {
setFiltered(info);
}
}
const gradePopulate = () => {
info && info.map((grades) => {
const listGrades = grades.grades;
setAverage(listGrades);
var a=0;
const p = Average.reduce((a,b) => a + b , a);
console.log(p);
});
}
return (
<div>
<Card className="card">
<CardContent className="scrollbar scrollbar-primary mt-5 mx-auto">
<Input
className="searchBar"
icon="search"
placeholder="Search by name"
onChange={filterChange}
value={input}
/>
{filtered.map((name) => (
<ul className = "border" key={name.id}>
<Grid item xs={3} sm={6} md={12} style={{display: "flex", gap:"3.5rem", paddingBottom:"8px"}}>
<img alt ="" src={name.pic} className="picture"></img>
<Grid container style={{display: "inline"}} align="left" justifyContent="flex-end" alignItems="flex-start">
<Grid className="studentNames">
<span>{name.firstName + " " + name.lastName}</span>
<Grid container align="right" justifyContent="flex-end" alignItems="flex-end" style={{display: "inline"}} >
<button onClick={gradePopulate} className="plus"></button></Grid>
</Grid>
<span>{name.email}</span>
<br/>
<span>{name.company}</span>
<br/>
<span>{name.skill}</span>
<br/>
<span>Average: {9}%</span>
</Grid>
</Grid>
</ul>
))}
</CardContent>
</Card>
</div>
)
}
export default StudentProfiles;
I don't understand how your data is structured but from your code, I am guessing
info = [{ grades: [] }, { grades: [] }]
and you want to sum the grades in each objects. If so, you can change your function to the below.
const store = {students: [{grades: ["78","100","92","86","89","88","91","87"]}, {grades: ["75","89","95","93","99","82","89","76"]}, {grades: ["88","90","79","82","81","99","94","73"]}, {grades: ["88","93","92","81","95","98","77","94"]}, {grades: ["71","81","72","92","79","82","91","90"]}, {grades: ["97","92","72","99","92","92","79","96"]}]};
const info = store.students
const gradePopulate = () => {
return info && info.map((grades) => {
const listGrades = grades.grades;
// setAverage(listGrades);
const p = listGrades.reduce((a,b) => a + Number(b) , 0);
return p
});
}
console.log(gradePopulate())
const store = {students: [{grades: ["78","100","92","86","89","88","91","87"]}, {grades: ["75","89","95","93","99","82","89","76"]}, {grades: ["88","90","79","82","81","99","94","73"]}, {grades: ["88","93","92","81","95","98","77","94"]}, {grades: ["71","81","72","92","79","82","91","90"]}, {grades: ["97","92","72","99","92","92","79","96"]}]};
const info = store.students
const gradePopulate = () => {
const totalSum = info.reduce((a, b) => a + b.grades.reduce((c, d) => c + Number(d), 0), 0);
const totalLength = info.reduce((a, b) => a + b.grades.length, 0)
return totalSum / totalLength
}
console.log(gradePopulate())
So my page is an Author page which shows different authors and their details in each card which I fetched from API and then mapped.
https://i.stack.imgur.com/eSD7u.png
And in each card after onclick it changes to Remove Favourite. The card which is favourited makes the idfav true in the object array of the author state and false if not favourited. And there is a 2nd page which shows all the favourite authors. Now I am passing it down first as localstorage for the author state but it seems after my 2nd reload if I click on the button irrespective of whether or not the button is add or remove all the other cards/array is removed and only the card on which button I selected shows up.
const [author, setAuthor] = useState([]);
const [AuthorTempState, setAuthorTempState] = useState([]);
// pagination calculation
const [PageNumber, setPageNumber] = useState(0);
const [Postsperpage] = useState(4);
const PagesVisited = PageNumber * Postsperpage;
const pageCount = Math.ceil(author.length / Postsperpage);
const changePage = ({ selected }) => {
setPageNumber(selected);
}
const getAuthors = async () => {
const res = await fetch(`https://api.quotable.io/authors?limit=30`);
const data = await res.json();
for (const element of data.results) {
element.idfav = false;
}
data.results.sort((a, b) => (a._id > b._id) ? 1 : -1)
setAuthor(data.results);
setAuthorTempState(data.results);
}
const saveAuth = () => {
localStorage.setItem('authors', JSON.stringify(author));
}
const getAuth = () => {
const newAuthors = JSON.parse(localStorage.getItem('authors'));
if (newAuthors && newAuthors.length > 0) {
setAuthor(newAuthors);
} else {
getAuthors();
}
}
useEffect(() => {
// console.log((author));
if (author.length === 0) {
getAuth();
}
saveAuth();
}, [author]);
const favBttn = (Auth) => {
const filterData = AuthorTempState.filter(data => data._id !== Auth._id)
Auth.idfav = true;
const updateAuthor = [Auth, ...filterData]
updateAuthor.sort((a, b) => (a._id > b._id) ? 1 : -1)
setAuthor(updateAuthor)
}
const remfavBttn = (Auth) => {
const filterData = AuthorTempState.filter(data => data._id !== Auth._id)
Auth.idfav = false;
const updateAuthor = [Auth, ...filterData]
updateAuthor.sort((a, b) => (a._id > b._id) ? 1 : -1)
setAuthor(updateAuthor);
}
const Author = author.slice(PagesVisited, PagesVisited + Postsperpage)
return (
<div className="AppWhole">
<AuthorSidebar />
<div className="App">
<div className="author">
{Author.map(
(Author) => (
<div className="authors" key={Author._id}>
{
(Author.idfav) ? (<button className='right' onClick={() => {
remfavBttn(Author);
}}>Remove Favt.</button >) : (<button className='right' onClick={() => {
favBttn(Author);
}}>Add Favt.</button >)
}
<p>Name: {Author.name}</p>
<p>Bio: {Author.bio}</p>
<p>Wiki: <a href='{Author.link}'>{Author.link}</a></p>
</div>
))}
<div id='pageauthor'>
<ReactPaginate
pageCount={pageCount}
onPageChange={changePage}
previousLabel={"<<"}
nextLabel={">>"}
containerClassName={'paginationLinks'}
disabledClassName={'paginationDisabled'}
activeClassName={'paginationActive'}
/>
</div>
</div>
</div>
</div>
);
}
export default Authors;
Please help me I have been stuck on this for a week. Thank you.
Okay, once I read your entire code and then read your issue made it pretty clear what's wrong. The issue is here
const favBttn = (Auth) => {
// notice that you are using AuthorTempState to filter data
// but do you remember initialising it when the data is found in local storage?
// AuthorTempState is currently an empty array.
const filterData = AuthorTempState.filter(data => data._id !== Auth._id)
Auth.idfav = true;
const updateAuthor = [Auth, ...filterData]
updateAuthor.sort((a, b) => (a._id > b._id) ? 1 : -1)
setAuthor(updateAuthor)
}
I am trying to manipulate the images shown in my React App by voice. I implemented the SR, which works fine:
<button onClick={SpeechRecognition.startListening}>Start</button>
I have an array of loadImages in my state, which is empty first:
const [loadImages, setLoadImages] = React.useState([]);
Whenever the word spoken is "kitten", the array of loadImages gets updated in this way:
if(transcript == "kitten")
{
const newImages = loadImages.concat({image: 'https://www.onlinekittencare.com/wp-content/uploads/2020/07/vChK6pTy3vN3KbYZ7UU7k3-1200-80.jpg'})
setLoadImages(newImages);
}
The transcript you see is a variable, which I initialized here:
const {transcript} = useSpeechRecognition();
In the render I use it to show what the SR understood, so if I say "hello" it shows "hello":
<p id="transcript">Transcript: {transcript}</p>
And this is where the images in loadImages show up:
{images.map((image) => {
return <URLImage image={image}/>;
})}
The problem is that whenever I say "kitten", which as stated above is used as a command to add the picture to the array loadImages, my React App gets blank. In the inspect I can also see that it says react-dom.development.js:14997 Uncaught Error: Too many re-renders. React limits the number of renders to prevent an infinite loop.
How do I fix that?
EDIT (I added the whole code):
function App() {
const [rectangles, setRectangles] = useState([]);
const [circles, setCircles] = useState([]);
const [selectedId, selectShape] = useState(null);
const [shapes, setShapes] = useState([]);
const [, updateState] = React.useState();
const stageEl = React.createRef();
const layerEl = React.createRef();
const fileUploadEl = React.createRef();
const [brushSize, setBrushSize] = React.useState('5');
const [isDrawing, setIsDrawing] = React.useState(false);
const dragUrl = React.useRef();
//const stageRef = React.useRef();
const [images, setImages] = React.useState([]);
const [loadImages, setLoadImages] = React.useState([]);
const getRandomInt = max => {
return Math.floor(Math.random() * Math.floor(max));
};
const {transcript} = useSpeechRecognition();
const URLImage = ({image}) => {
const [img] = useImage(image.src);
return (
<Image
image = {img}
x = {image.x}
y = {image.y}
offsetX = {50}
offsetY = {50}
width={200}
height={200}
draggable
/>
);
};
const drawLine = () => {
setIsDrawing(true);
if(isDrawing){
addLine(stageEl.current.getStage(), layerEl.current, brushSize);
};
};
const eraseLine = () => {
addLine(stageEl.current.getStage(), layerEl.current, brushSize, "erase");
};
const addRectangle = () => {
setIsDrawing(false);
const rect = {
x: getRandomInt(100),
y: getRandomInt(100),
width: 100,
height: 100,
fill: "red",
id: `rect${rectangles.length + 1}`,
};
const rects = rectangles.concat([rect]);
setRectangles(rects);
const shs = shapes.concat([`rect${rectangles.length + 1}`]);
setShapes(shs);
};
const forceUpdate = React.useCallback(() => updateState({}), []);
const undo = () => {
const lastId = shapes[shapes.length - 1];
let index = circles.findIndex(c => c.id == lastId);
if (index != -1) {
circles.splice(index, 1);
setCircles(circles);
}
index = rectangles.findIndex(r => r.id == lastId);
if (index != -1) {
rectangles.splice(index, 1);
setRectangles(rectangles);
}
index = images.findIndex(r => r.id == lastId);
if (index != -1) {
images.splice(index, 1);
setImages(images);
}
shapes.pop();
setShapes(shapes);
forceUpdate();
};
document.addEventListener("keydown", ev => {
if (ev.code == "Delete") {
let index = circles.findIndex(c => c.id == selectedId);
if (index != -1) {
circles.splice(index, 1);
setCircles(circles);
}
index = rectangles.findIndex(r => r.id == selectedId);
if (index != -1) {
rectangles.splice(index, 1);
setRectangles(rectangles);
}
index = images.findIndex(r => r.id == selectedId);
if (index != -1) {
images.splice(index, 1);
setImages(images);
}
forceUpdate();
}
});
if(transcript == "kitten")
{
const newImages = loadImages.concat({image: 'https://www.onlinekittencare.com/wp-content/uploads/2020/07/vChK6pTy3vN3KbYZ7UU7k3-1200-80.jpg'})
setLoadImages(newImages);
}
return (
<div className="home-page">
{loadImages.map(image => (
<img id="img" className="img"
src={image.image}
width="200"
height="200"
onDragStart={(e) => {
dragUrl.current = e.target.src;}}
/>
))}
<div
onDrop={(e) => {
e.preventDefault();
// register event position
stageEl.current.setPointersPositions(e);
// add image
setImages(
images.concat([
{
...stageEl.current.getPointerPosition(),
src: dragUrl.current,
},
])
);
}}
onDragOver={(e) =>
e.preventDefault()
}
>
<h1>Whiteboard</h1>
<button onClick={addRectangle}>
Rectangle
</button>
<button>
Circle
</button>
<button onClick={drawLine}>
Line
</button>
<button onClick={eraseLine}>
Erase
</button>
<select
value={brushSize}
onChange={(e) => {
setBrushSize(e.target.value);
drawLine();
}}
>
<option value="5">5</option>
<option value="20">20</option>
<option value="50">50</option>
</select>
<button variant="secondary">
Text
</button>
<button variant="secondary">
Image
</button>
<button variant="secondary" onClick={undo}>
Undo
</button>
<p id="transcript">Transcript: {transcript}</p>
<button onClick={SpeechRecognition.startListening}>Start</button>
<Stage
width={window.innerWidth * 0.9}
height={window.innerHeight - 150}
ref={stageEl}
dragabble
onMouseDown={e => {
// deselect when clicked on empty area
const clickedOnEmpty = e.target === e.target.getStage();
if (clickedOnEmpty) {
selectShape(null);
}
}}
>
<Layer ref={layerEl}>
{rectangles.map((rect, i) => {
return (
<Rectangle
key={i}
shapeProps={rect}
isSelected={rect.id === selectedId}
//onSelect={() => {
// selectShape(rect.id);
//}}
onChange={newAttrs => {
const rects = rectangles.slice();
rects[i] = newAttrs;
setRectangles(rects);
}}
/>
);
})}
{images.map((image) => {
return <URLImage image={image}/>;
})}
</Layer>
</Stage>
</div>
</div>
);
}
export default App;
Based on the code you've shared, it has to do with how you're updating the state if the transcript is equal to kitten.
Essentially, the logic you've written says, on render, if the transcript is kitten, update the state. BUT, when you update the state, that will re-render, and hit that logic again... and again... and again. The solution here is to wrap that in a useEffect – React Docs explain it best but in simple terms, you want to "do something" as a side effect of "something else".
In this case, if the transcript updates, you want to check the state of transcript, and if it meets a condition, you want to update your state:
React.useEffect(() => {
if (transcript === "kitten") {
const newImages = loadImages.concat({image: 'https://www.onlinekittencare.com/wp-content/uploads/2020/07/vChK6pTy3vN3KbYZ7UU7k3-1200-80.jpg'})
setLoadImages(newImages);
}
}, [transcript]);
The final piece to the useEffect is a dependency array ([transcript])–this dictates to React which item you want to watch for changes in–if transcript changes, it will run your effect and only when it changes, instead of every time it renders.