ReactJs only rendering one img element from mapping an array of URLs - reactjs

I am reading image data from firebase storage and getting the URLs of the images in an array.
I console logged the array. It is fine.
I made a variable of img elements through map() function on that array.
That variable is also fine.
But I am not able to render more than one in the component. Only the last image tag renders from the array.
import React, { useRef, useState, useEffect } from 'react'
import { Card, Button, Alert } from 'react-bootstrap'
import { Link, useHistory } from 'react-router-dom'
import { UseAuth } from '../context/AuthContex'
import app from './../firebase'
import { db } from './../firebase'
import './Dashboard.css'
function Dashboard() {
const [error, setError] = useState('')
const { currentUser, logout } = UseAuth()
const history = useHistory()
const picURLS = []
const [photo, setPhoto] = useState()
async function getPics() {
const DBRef = db.collection('pics');
const snapshot = await DBRef.where('author', '==', currentUser.uid).get();
if (snapshot.empty) {
console.log('No matching documents.');
return;
}
snapshot.forEach(doc => {
var storage = app.storage();
var gsReference = storage.refFromURL('<bucket address>' + doc.data().filename)
gsReference.getDownloadURL().then(function (url) {
picURLS.push(url);
}).catch(function (error) {
console.log(error)
});
});
}
useEffect(() => {
getPics()
console.log('getPIcs() triggered.')
console.log(picURLS)
setPhoto(picURLS.map(postdata => (
<img className='photoOfOrder' key={postdata} src={postdata} alt={postdata} />)))
console.log(photo)
}, [])
return (
<div>
<div>{photo}</div>
<div className="menu pmd-floating-action" role="navigation">
<Link to='/upload-pic' className="pmd-floating-action-btn btn pmd-btn-fab pmd-btn-raised pmd-ripple-effect btn-primary" data-title="Splash New Image?" href="javascript:void(0);">
<span className="pmd-floating-hidden">Primary</span>
<i className="material-icons pmd-sm">add</i>
</Link>
</div>
</div>
)
}
export default Dashboard

It would be better to add picURLS to your state variables because useEffect runs only once with empty dependencies array and setPhoto(picURLS.map) surely would work with an empty picURLS array before it will be filled. So your photo var surely would be empty. You should call your map in the render function;
{picURLS.map(postdata => (
<img className='photoOfOrder' key={postdata} src={postdata} alt={postdata} />))}
Try this code
import React, { useRef, useState, useEffect } from 'react'
import { Card, Button, Alert } from 'react-bootstrap'
import { Link, useHistory } from 'react-router-dom'
import { UseAuth } from '../context/AuthContex'
import app from './../firebase'
import { db } from './../firebase'
import './Dashboard.css'
function Dashboard() {
const [error, setError] = useState('')
const { currentUser, logout } = UseAuth()
const history = useHistory()
const [picURLS, setPicURLS] = useState([])
async function getPics() {
const DBRef = db.collection('pics');
const snapshot = await DBRef.where('author', '==', currentUser.uid).get();
if (snapshot.empty) {
console.log('No matching documents.');
return;
}
const newUrls = [];
snapshot.forEach(doc => {
var storage = app.storage();
var gsReference = storage.refFromURL('<bucket address>' + doc.data().filename)
gsReference.getDownloadURL().then(function (url) {
newUrls.push(url);
}).catch(function (error) {
console.log(error)
});
});
setPicURLS(newUrls);
}
useEffect(() => {
getPics()
console.log('getPIcs() triggered.')
console.log(picURLS)
}, [])
return (
<div>
<div>{
picURLS.map(postdata => (
<img className='photoOfOrder' key={postdata} src={postdata} alt={postdata} />))}
</div>
<div className="menu pmd-floating-action" role="navigation">
<Link to='/upload-pic' className="pmd-floating-action-btn btn pmd-btn-fab pmd-btn-raised pmd-ripple-effect btn-primary" data-title="Splash New Image?" href="javascript:void(0);">
<span className="pmd-floating-hidden">Primary</span>
<i className="material-icons pmd-sm">add</i>
</Link>
</div>
</div>
)
}
export default Dashboard

photo is array of React components. you need to loop over photo using map array method again to show in render.
{photo.map((img) => { img }}

Related

i am not able to show data from firebase in my react app

I want to show categories of items in div with class name items but this div is not rendering on screen i can only see section tag on inspect please explain what i am doing wrong
import { useEffect } from "react";
import { useState } from "react";
import firebase from "../../Firebase.config";
import './CategoriesPage.css'
const CategoriesPage = () => {
const ref = firebase.firestore().collection("Categories")
const [data, setdata] = useState([])
const [loader, setloader] = useState(true)
function getData() {
const items = []
ref.onSnapshot((querySnapshot) => {
querySnapshot.forEach((doc) => {
items.push(doc.data())
})
})
setdata(items)
setloader(false)
}
useEffect(() => {
getData()
}, [])
return (
<section className="Categoriebox">{
loader === true && [data].map((dev) => {
<div className="items" key={dev.id}>
<h1>{dev.name}</h1>
</div>
})
}
</section>
)
};
export default CategoriesPage;

Why my Firebase get() function fires twice inside React component?

In the initialize file, I code like this.
import { initializeApp } from 'firebase/app';
import { getDatabase, ref, child, get } from "firebase/database";
const config = {
};
const app = initializeApp(config);
const db = getDatabase(app);
const dbRef = (ref(db));
get(child(dbRef, "shop_data")).then((snap) => {
console.log(snap.val())
})
export {dbRef};
From here, I receive one result from console.log
Now, in my Store component, I only put the get() function in
import '../css/StoreSelectable.css';
import { dbRef } from '../js/firebase_init';
import { getDatabase, ref, child, get } from "firebase/database";
function StoreSelectable(){
const getStore = () => {
get(child(dbRef, "shop_data")).then((snap) => {
console.log(snap.val())
})
return;
}
return(
<div className="Store-Selectable">
<table id="place_option" align="center" style={{tableLayout: 'fixed'}} className="radioButtons collapseBorder">
<tbody>
{getStore()}
</tbody>
</table>
</div>
);
}
export default StoreSelectable;
Now, firebase function fires twice.
Edit 10/6/2022
I tried useEffect, but it still gets the data twice. I really do not want to use Firestore since I have to rewrite a lot of codes. What should I do in this situation?
import "../css/StoreSelectable.css";
import { dbRef } from "../js/firebase_init";
import { getDatabase, ref, child, get } from "firebase/database";
import { useEffect, useState } from "react";
function StoreSelectable() {
const pin = {
TestiKirppis: ["Jarii1", "spr1234", "6899bc73da4ace09"],
Vaasa: ["D0ED5D57F47580F2", "spr9876", "Vas183"],
Seinäjoki: ["a1a1a1a1a1a1a1a1", "spr9999", "Seina19"],
Kokkola: ["regT863", "spr0000", "b4b8bb4ceeaa2aee"],
};
const [count, setCount] = useState([]);
useEffect(() => {
const getStore = () => {
get(child(dbRef, "shop_data")).then((snap) => {
let val = snap.val();
Object.keys(val).forEach((key) => {
setCount((count) => [...count, val[key].name]);
})
});
}
getStore();
}, []);
return (
<div className="Store-Selectable">
<table
id="place_option"
align="center"
style={{ tableLayout: "fixed" }}
className="radioButtons collapseBorder"
>
<tbody>{count.map((data) => {return (<p>{data}</p>)})}</tbody>
</table>
</div>
);
}
export default StoreSelectable;
I think it is because of strict mode in react 18. If you remove it, the issue will be resolved.
Please check : multiple execution of files leading to multiple server calls in react js

map array of objects from an api react

I have been trying to get the images from this API to append to the page by mapping through them, but I keep getting one of two error messages saying 'undefined.map is not a function' or 'getBirds.map is not a function.'I've tried leaving the array of objects as it is and setting state to an object and an array(at separate times) but that didn't work. I've also tried using Object.key, Object.values, and Object.entries(each at separate times) to turn the array of objects into an array and then map through my variable and through getBirds(again separately) but those attempts also failed. I have attached three of my attempts below. Can someone help me understand where I've gone wrong?
// Attempt 1
import {useState, useEffect} from 'react'
import axios from 'axios'
function Birds(props) {
const [getBirds, setGetBirds] = useState({})
const {image} = props
useEffect(() => {
async function fetchBirds() {
const URL = `https://audubon-society-api.herokuapp.com/birds`
try {
const res = await axios.get(URL)
console.log(res.data)
setGetBirds(res.data)
} catch (error) {
console.log(error)
}
}
fetchBirds()
}, [])
if (!getBirds) return <h3>Loading...</h3>
return (
<div>
<img src={getBirds.map(image)} alt={getBirds.map(image)}></img>
</div>
)
}
export default Birds
// Attempt 2
import {useState, useEffect} from 'react'
import axios from 'axios'
function Birds(props) {
const [getBirds, setGetBirds] = useState([])
const {image} = props
useEffect(() => {
async function fetchBirds() {
const URL = `https://audubon-society-api.herokuapp.com/birds`
try {
const res = await axios.get(URL)
console.log(res.data)
setGetBirds(res.data)
} catch (error) {
console.log(error)
}
}
fetchBirds()
}, [])
if (!getBirds) return <h3>Loading...</h3>
return (
<div>
<img src={getBirds.map(image)} alt={getBirds.map(image)}></img>
</div>
)
}
export default Birds
// Attempt 3
import {useState, useEffect} from 'react'
import axios from 'axios'
function Birds(props) {
const [getBirds, setGetBirds] = useState({})
const {image} = props
useEffect(() => {
async function fetchBirds() {
const URL = `https://audubon-society-api.herokuapp.com/birds`
try {
const res = await axios.get(URL)
console.log(res.data)
setGetBirds(res.data)
} catch (error) {
console.log(error)
}
}
fetchBirds()
}, [])
const birds = Object.entries(getBirds)
birds.forEach(([key, value]) => {
console.log(key, value)
})
if (!getBirds) return <h3>Loading...</h3>
return (
<div>
<img src={birds.map(image)} alt={birds.map(image)}></img>
</div>
)
}
export default Birds
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.6.3/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.6.3/umd/react-dom.production.min.js"></script>
You would need to initialize your state with an array, so the map function won't get errors, and correct the way you map it:
Initialize state with an array:
const [getBirds, setGetBirds] = useState([]);
Map it:
return (
<div>
{getBirds.map((bird) => (
<img src={bird.image} alt={bird.image}></img>
))}
</div>
);
Also, check your array with length, because [] or {} both equal to true.
if (!getBirds.length) return <h3>Loading...</h3>;
console.log(!![]);
console.log(!!{});
console.log(!![].length)
The completed solution:
import { useState, useEffect } from "react";
import axios from "axios";
function Birds(props) {
const [getBirds, setGetBirds] = useState([]);
useEffect(() => {
async function fetchBirds() {
const URL = 'https://audubon-society-api.herokuapp.com/birds';
try {
const res = await axios.get(URL);
console.log(res.data);
setGetBirds(res.data);
} catch (error) {
console.log(error);
}
}
fetchBirds();
}, []);
if (!getBirds.length) return <h3>Loading...</h3>;
return (
<div>
{getBirds.map((bird) => (
<img src={bird.image} alt={bird.image}></img>
))}
</div>
);
}
export default Birds;
Working Example:
Your init state of birds and setBirds should be an array [] not an object {}, also you don't need:
const birds = Object.entries(getBirds). fetch return array of birds already.
<img src={birds.map(image)} alt={birds.map(image)}></img> is wrong, the array loop map should render an image for each bird.
Below code will run for your need:
import React, {useState, useEffect} from "react";
import axios from 'axios';
function Birds(props) {
//- const [getBirds, setGetBirds] = useState([])
//- const {image} = props
// +
const [birds, setGetBirds] = useState([])
useEffect(() => {
async function fetchBirds() {
const URL = `https://audubon-society-api.herokuapp.com/birds`
try {
const res = await axios.get(URL)
console.log(res.data)
setGetBirds(res.data)
} catch (error) {
console.log(error)
}
}
fetchBirds()
}, [])
// - const birds = Object.entries(getBirds)
// - birds.forEach(([key, value]) => {
// - console.log(key, value)
// - })
// - if (!getBirds) return <h3>Loading...</h3>
if (!birds) return <h3>Loading...</h3>
return (
<div>
{/* <img src={birds.map(image)} alt={birds.map(image)}></img> */}
{birds.map((item, index) =>
<img src={item.image} alt={index}></img>
)}
</div>
)
}
export default Birds

dispatch inside useEffect opposite result

I am displaying a foto in the front using Leigh Halliday's Image Previews in React with FileReader from https://www.youtube.com/watch?v=BPUgM1Ig4Po and everything is super BUT:
1.I want to get information from the image is displaying, exactly the base64 info, and have it then globally in my reactjs app.
2.for that reason I made a Context, i configured it ok BUT:
when I am doing dispatch inside a useEffect I want the image rendering and the info store in my variable globally
but I have one thing or another
if my image renders ok in my front, I can not obtain the value of my dispatch and viceversa
this is the code of my component:
import React, { useContext, useEffect, useRef, useState } from 'react'
import { AuthContext } from '../../auth/AuthContext'
import { types } from '../../types/types'
export const ButtonLoadFoto = () => {
const { dispatchFoto } = useContext(AuthContext)
const [image, setImage] = useState('')
const [preview, setPreview] = useState('')
const [status, setStatus] = useState(false)
useEffect(() => {
if (image) {
const reader = new FileReader()
reader.onloadend = () => {
setPreview(reader.result)
}
reader.readAsDataURL(image)
setStatus(true)
} else {
setPreview('')
}
}, [image])
// useEffect(() => {
// if (status) {
// dispatchFoto({
// type: types.foto,
// payload: {
// foto: preview.split(',')[1]
// }
// })
// }
// return () => setStatus(false)
// }, [preview])
const fileInputRef = useRef()
const handleRef = (e) => {
e.preventDefault()
fileInputRef.current.click()
}
const handleFile = (e) => {
const file = e.target.files[0]
if (file && file.type.substr(0, 5) === 'image') {
setImage(file)
}
}
return (
<div className='load-input '>
{
preview
?
(<img src={preview} alt='' onClick={() => setImage('')} />)
:
(<button
className='alert alert-danger'
onClick={handleRef}>
foto
</button>
)
}
< input
type='file'
style={{ display: 'none' }}
ref={fileInputRef}
accept='image/*'
onChange={handleFile}
/>
</div>
)
}
in the code above if you put away the comments we will have the information we want but the foto won t display at all
thanks all for your time , I really appreciate!
EDIT
this is the main component
import React, { useEffect, useReducer } from 'react'
import { AuthContext } from './auth/AuthContext'
import { fotoReducer } from './components/formScreen/fotoReducer'
import { AppRouter } from './routers/AppRouter'
const initImage = () => {
return { foto: '' }
}
export const CMI = () => {
const [foto, dispatchFoto] = useReducer(fotoReducer, {}, initImage)
return (
<div>
<AuthContext.Provider value={{
foto,
dispatchFoto
}}>
<AppRouter />
</AuthContext.Provider>
</div>
)
}
this is the componenent I use
import React, { useContext} from 'react'
import { ButtonLoadFoto } from '../components/formScreen/ButtonLoadFoto'
import { AuthContext } from '../auth/AuthContext'
export const FormScreen = () => {
const { foto } = useContext(AuthContext)
}
return (
<div>
<ButtonLoadFoto/>
</div>
)
as I said : if a render the image I can not have the information and viceversa...
when I use dispatch I don t know I it brokes the image render
thanks in advance

React custom hook state not 'always there'

I thought had a better grasp of hooks but I've clearly got something wrong here. Not all of the character objects will have what I'm trying to get but it wont work with those that do.
I cna't even build in a check for character.comics.available. Same errors appear. I'm presuming I'm getting them before the state is set? But {character.name} always works.
CharacterDetail.js
import React from "react";
import { useParams } from "react-router-dom";
import useCharacter from "../hooks/useCharacter";
const CharacterDetail = () => {
// from the Route path="/character/:id"
const { id } = useParams();
// custom hook. useCharacter.js
const [character] = useCharacter(id);
// this only works sometimes but errors if i refresh the page
// console.log(character.comics.available);
return (
<div>
<h2 className="ui header">Character Details</h2>
<p>Works every time: {character.name}</p>
<div className="ui segment"></div>
<pre></pre>
</div>
);
};
export default CharacterDetail;
Custom hook useCharacter.js
import { useState, useEffect } from "react";
import marvel from "../apis/marvel";
const useCharacter = (id) => {
const [character, setCharacter] = useState({});
useEffect(() => {
loadItem();
return () => {};
}, [id]);
const loadItem = async (term) => {
const response = await marvel.get(`/characters/${id}`);
console.log(response.data.data.results[0]);
setCharacter(response.data.data.results[0]);
};
return [character];
};
export default useCharacter;
error when console is uncommented
Uncaught TypeError: Cannot read property 'available' of undefined
at CharacterDetail (CharacterDetail.js:11)
...
Here is the character object.
thanks to #Nikita for the pointers. Settled on this...
CharacterDetail.js
import React from "react";
import { useParams } from "react-router-dom";
import useCharacter from "../hooks/useCharacter";
const CharacterDetail = () => {
const { id } = useParams();
// custom hook. useCharacter.js
const { isLoading, character } = useCharacter(id);
const isArray = character instanceof Array;
if (!isLoading && isArray === false) {
console.log("isLoading", isArray);
const thumb =
character.thumbnail.path +
"/portrait_uncanny." +
character.thumbnail.extension;
return (
<div>
<h2 className="ui header">{character.name}</h2>
<img src={thumb} />
<div className="ui segment">{character.comics.available}</div>
<div className="ui segment">{character.series.available}</div>
<div className="ui segment">{character.stories.available}</div>
</div>
);
}
return <div>Loading...</div>;
};
export default CharacterDetail;
useCharacter.js
import { useState, useEffect } from "react";
import marvel from "../apis/marvel";
function useCharacter(id) {
const [character, setCharacter] = useState([]);
const [isLoading, setIsLoading] = useState(false);
useEffect(() => {
setIsLoading(true);
const fetchData = async () => {
setIsLoading(true);
await marvel
.get(`/characters/${id}`)
.then((response) => {
/* DO STUFF WHEN THE CALLS SUCCEEDS */
setIsLoading(false);
setCharacter(response.data.data.results[0]);
})
.catch((e) => {
/* HANDLE THE ERROR (e) */
});
};
fetchData();
}, [id]);
return {
isLoading,
character,
};
}
export default useCharacter;

Resources