.map is not a function in React production - javascript

When running my express react app build locally the map function works flawlessly, however whenever I switch to production. I get the error that map is not a function.
Error comes up pointing towards 'let returnlist' line. I have included the index.js and where the error occurs.
import React, { useState, useEffect } from 'react'
import ReactDOM from 'react-dom'
import AddNewPerson from './component/addNew'
import Filter from './component/filter'
import Persons from './component/persons'
import axios from 'axios'
const App = () => {
const [persons, setPersons] = useState([])
const [tempPersonList, setTempPersonList] = useState([])
const [ newName, setNewName ] = useState('')
const [ newNumber, setNewNumber] = useState('')
let searchInput = false;
useEffect(() => {
axios
.get('http://localhost:3001/init')
.then(response => {
console.log(response.data)
setPersons(response.data)
setTempPersonList(response.data)
})
}, [])
return (
<div>
<Filter persons = {persons} setPersons= {setPersons} tempPersonList = {tempPersonList}
searchInput = {searchInput}/>
<AddNewPerson persons = {persons} setPersons= {setPersons} newName = {newName}
setNewName = {setNewName} setNewNumber = {setNewNumber} newNumber = {newNumber} />
<Persons persons = {persons} setPersons = {setPersons} tempPersonList = {tempPersonList} searchInput = {searchInput} />
...
</div>
)
}
ReactDOM.render(<App />, document.getElementById('root'))
The error occurs in the code below on 'let returnList'.
const Persons = (props) => {
const persons = props.persons
const setPersons = props.setPersons
const tempPersonList = props.tempPersonList
let searchInput = props.searchInput
let returnList = persons.map((person) => <li className='contact' key = {person.number}>{person.name} : {person.number} <button onClick={
() =>{ Axios.delete(`${url}/${person.id}`)
.then((response) =>{
setPersons(persons.filter(personCheck => personCheck.id !== person.id ))
})}}>Remove</button></li>)
let returnSearchList = tempPersonList.map((person) => <li key = {person.number}>{person.name} : {person.number} <button onClick={
() =>{ Axios.delete(`${url}/${person.id}`)
.then((response) =>{
setPersons(persons.filter(personCheck => personCheck.id !== person.id ))
})}}>Remove</button></li>)
return(
<div>
<h2>
Numbers
</h2>
{searchInput ? returnSearchList : returnList}
</div>
)
}
export default Persons
Many Thanks

Your response data doesn't return an array, it's probably an object. Check in the network tab of your browser what your AJAX call returns.
Or just open this in your browser: http://localhost:3001/init
The data field of whatever is shown has to be an array.

Related

Pagination with React doesn't work, all items are still displayed on screen

I have a pagination made with a react-paginate package called react-paginate here is the link to the doc. https://www.npmjs.com/package/react-paginate
I have implemented it in my App which is a notes diary, the user creates notes and these are dynamically saved in the localStorage and displayed on screen, well, I have established that there are 6 notes per page, that is, when there is a seventh note, it should not be displayed unless the user goes to page 2, when there are 13 notes page 3 and so ...
The functionality of my component that I have called Pagination works correctly, it is dynamic, I have right now testing 13 notes, so it shows me 3 pages, if I had 12, it would show me 2.
The problem is that although my pagination is correct, the 13 notes are being shown on the screen, when it should be 6 - 6 - 1.
I leave you the code to see if we can find the error, greetings and thanks in advance.
The prop that Pagination receives called data, are basically the notes that are created dynamically in App.js. const [notes, setNotes] = useState([]);
Component Pagination
import React, { useEffect, useState } from 'react'
import ReactPaginate from 'react-paginate';
import '../styles/Pagination.css';
const Pagination = (props) => {
const { data } = props;
// We start with an empty list of items.
const [currentItems, setCurrentItems] = useState([]);
const [pageCount, setPageCount] = useState(0);
// Here we use item offsets; we could also use page offsets
// following the API or data you're working with.
const [itemOffset, setItemOffset] = useState(0);
const itemsPerPage = 6;
useEffect(() => {
// Fetch items from another resources.
const endOffset = itemOffset + itemsPerPage;
console.log(`Loading items from ${itemOffset} to ${endOffset}`);
setCurrentItems(data.slice(itemOffset, endOffset));
setPageCount(Math.ceil(data.length / itemsPerPage));
}, [itemOffset, itemsPerPage, data]);
// Invoke when user click to request another page.
const handlePageClick = (event) => {
const newOffset = (event.selected * itemsPerPage) % data.length;
console.log(
`User requested page number ${event.selected}, which is offset ${newOffset}`
);
setItemOffset(newOffset);
};
return (
<>
<ReactPaginate
breakLabel="..."
nextLabel="next >"
onPageChange={handlePageClick}
pageRangeDisplayed={3}
pageCount={pageCount}
previousLabel="< previous"
renderOnZeroPageCount={null}
containerClassName="pagination"
pageLinkClassName="page-num"
previousLinkClassName="page-num"
nextLinkClassName="page-num"
activeLinkClassName="activee boxx"
/>
</>
);
}
export default Pagination;
Component App
import { useState, useEffect } from "react";
import { nanoid } from 'nanoid';
import NoteList from "./components/NoteList";
import './App.css';
import Search from "./components/Search";
import Header from "./components/Header";
import Pagination from "./components/Pagination";
const App = () => {
const [notes, setNotes] = useState([]);
const [searchText, setSearchText] = useState('');
const [darkMode, setDarkMode] = useState(false);
//Se encarga de mostrar la nota para escribir
const [showNote, setShowNote] = useState(true); //eslint-disable-line
useEffect(() => {
const saveNotes = JSON.parse(localStorage.getItem('notes-data'));
if (saveNotes){
setNotes(saveNotes);
}
}, []);
useEffect(() => {
localStorage.setItem('notes-data', JSON.stringify(notes))
},[notes])
const addNote = (inputText, text) => {
const date = new Date();
const newNote = {
id: nanoid(),
title: inputText,
text: text,
date: date.toLocaleString()
}
const newNotes = [newNote, ...notes];
setNotes(newNotes)
}
const deleteNote = (id) => {
var response = window.confirm("Are you sure that you want to remove the note?");
if (response){
const notesUpdated = notes.filter((note) => note.id !== id)
setNotes(notesUpdated);
}
}
return (
<div className={darkMode ? 'dark-mode' : ''}>
<div className="container">
<Header
handleToggleTheme={setDarkMode}
/>
<Search
handleSearchNote={setSearchText}
setShowNote={setShowNote}
/>
<NoteList
notes={notes.filter((noteText) =>
noteText.title.toLowerCase().includes(searchText)
)}
handleAddNote={addNote}
handleDeleteNote={deleteNote}
/>
<Pagination data={notes}/>
</div>
</div>
)
}
export default App;
The problem is you are not using currentItems and the paginated data is stored in that state.
Codesandbox: https://codesandbox.io/s/sweet-keldysh-2u72vd
Pagination.js
import React, { useEffect, useState } from 'react'
import ReactPaginate from 'react-paginate';
import NoteList from "./components/NoteList";
import '../styles/Pagination.css';
const Pagination = (props) => {
const { data, searchText, handleAddNote, handleDeleteNote } = props;
// We start with an empty list of items.
const [currentItems, setCurrentItems] = useState([]);
const [pageCount, setPageCount] = useState(0);
// Here we use item offsets; we could also use page offsets
// following the API or data you're working with.
const [itemOffset, setItemOffset] = useState(0);
const itemsPerPage = 6;
useEffect(() => {
// Fetch items from another resources.
const endOffset = itemOffset + itemsPerPage;
console.log(`Loading items from ${itemOffset} to ${endOffset}`);
setCurrentItems(data.slice(itemOffset, endOffset));
setPageCount(Math.ceil(data.length / itemsPerPage));
}, [itemOffset, itemsPerPage, data]);
// Invoke when user click to request another page.
const handlePageClick = (event) => {
const newOffset = (event.selected * itemsPerPage) % data.length;
console.log(
`User requested page number ${event.selected}, which is offset ${newOffset}`
);
setItemOffset(newOffset);
};
return (
<>
<NoteList
notes={currentItems.filter((noteText) =>
noteText.title.toLowerCase().includes(searchText)
)}
handleAddNote={handleAddNote}
handleDeleteNote={handleDeleteNote}
/>
<ReactPaginate
breakLabel="..."
nextLabel="next >"
onPageChange={handlePageClick}
pageRangeDisplayed={3}
pageCount={pageCount}
previousLabel="< previous"
renderOnZeroPageCount={null}
containerClassName="pagination"
pageLinkClassName="page-num"
previousLinkClassName="page-num"
nextLinkClassName="page-num"
activeLinkClassName="activee boxx"
/>
</>
);
}
export default Pagination;
App.js
import { useState, useEffect } from "react";
import { nanoid } from 'nanoid';
import './App.css';
import Search from "./components/Search";
import Header from "./components/Header";
import Pagination from "./components/Pagination";
const App = () => {
const [notes, setNotes] = useState([]);
const [searchText, setSearchText] = useState('');
const [darkMode, setDarkMode] = useState(false);
//Se encarga de mostrar la nota para escribir
const [showNote, setShowNote] = useState(true); //eslint-disable-line
useEffect(() => {
const saveNotes = JSON.parse(localStorage.getItem('notes-data'));
if (saveNotes){
setNotes(saveNotes);
}
}, []);
useEffect(() => {
localStorage.setItem('notes-data', JSON.stringify(notes))
},[notes])
const addNote = (inputText, text) => {
const date = new Date();
const newNote = {
id: nanoid(),
title: inputText,
text: text,
date: date.toLocaleString()
}
const newNotes = [newNote, ...notes];
setNotes(newNotes)
}
const deleteNote = (id) => {
var response = window.confirm("Are you sure that you want to remove the note?");
if (response){
const notesUpdated = notes.filter((note) => note.id !== id)
setNotes(notesUpdated);
}
}
return (
<div className={darkMode ? 'dark-mode' : ''}>
<div className="container">
<Header
handleToggleTheme={setDarkMode}
/>
<Search
handleSearchNote={setSearchText}
setShowNote={setShowNote}
/>
<Pagination data={notes} handleAddNote={addNote}
handleDeleteNote={deleteNote} searchText={searchText} />
</div>
</div>
)
}
export default App;

Cannot fix "Uncaught TypeError: posts.map is not a function" error

I am creating a sns-like web application. As one of the functions, I am trying to display all posts users made. However, my code shows nothing and get an error on console saying "Uncaught TypeError: posts.map is not a function". I am totally a beginner in Javascript, react and firebase. Could anyone look into my code? Thank you.
import React, { useState, useEffect } from 'react';
import "./Post.css";
import Posts from "./Posts.js";
import { getFirestore } from "firebase/firestore";
import { collection, doc, onSnapshot } from "firebase/firestore";
import { useNavigate } from "react-router-dom";
import ImageUpload from "./ImageUpload.js";
function Post( {user} ) {
const db = getFirestore();
const navigate = useNavigate("");
const [posts, setPosts] = useState('');
const colRef = collection(db, 'posts');
useEffect(()=>
onSnapshot(colRef,(snapshot) => {
setPosts(
snapshot.docs.map((doc) => {
return{
post: doc.data(),
id: doc.id
};
})
);
}),
[]);
return (
<div className = "post">
<ImageUpload username = {user?.displayName} />
{
posts.map(({id, post}) => (
<Posts key = {id}
postId = {id}
origuser = {user?.displayName}
username = {post.username}
userId = {user.uid}
caption = {post.caption}
imageUrl = {post.imageUrl}
noLikes = {post.noLikes}
/>
))
}
</div>
)
}
export default Post
The first step is to initialise posts as an array, currently you have it as a string and in the string prototype there is no .map function.
const [posts, setPosts] = useState([]);
After that you need to make sure that in the setPosts call you also pass an array. By looking at the example it seems like already it is snapshot.docs.map.
Try this :
{
posts && posts.map(({id, post}) => (
<Posts key = {id}
postId = {id}
origuser = {user?.displayName}
username = {post.username}
userId = {user.uid}
caption = {post.caption}
imageUrl = {post.imageUrl}
noLikes = {post.noLikes}
/>
))
}
define your state as a array
const [posts, setPosts] = useState([]);
then add extra validation layer on the map method
{
posts && posts.length > 0 && posts.map(({id, post}) => (
<Posts key = {id}
postId = {id}
origuser = {user?.displayName}
username = {post.username}
userId = {user.uid}
caption = {post.caption}
imageUrl = {post.imageUrl}
noLikes = {post.noLikes}
/>
))
}

I get data from Api but when I try to set the value with useState it give me an empty object

Hello everyone newbie here! I'm trying to use UseState hook to set setForecastData to the result that I receive from the API call, so then I will be able to access to it and use it elsewhere.
After I set a new value to the hook I try to console.log it and it still give me an empty object!
I don't know what Im doing wrong in here ? any suggestion is appreciate thanks!
( please see comments on code )
import axios from "axios";
import "./App.css";
import HomePage from "./Components/HomePage";
import MainPage from "./Components/MainPage";
const App = () => {
const apiKey = process.env.REACT_APP_API_KEY;
const [input, setInput] = useState("");
const [city, setCity] = useState("");
const [matchesArray, setMatchesArray] = useState([]);
const [locationData, setLocationData] = useState();
const [forecastData, setForecastData] = useState({});
//get today weather info
const getCurrentWeather = () => {
axios
.get(
`https://api.openweathermap.org/data/2.5/weather?q=${city}&units=metric&APPID=${apiKey}`
)
.then((res) => {
console.log(res.data);
let resp = res.data;
setLocationData(resp);
})
.catch((err) => console.log(err));
};
//get weekly weather info
const getWeeklyWeather = () => {
let lat = matchesArray[0].lat;
let lon = matchesArray[0].long;
axios
.get(
`https://api.openweathermap.org/data/2.5/onecall?lat=${lat}&lon=${lon}&exclude=minutely,current&units=metric&appid=${apiKey}`
)
.then((res) => {
const utcOffset = res.data.timezone_offset;
const hourly = res.data.hourly;
const daily = res.data.daily;
let hourlyReduced = hourly.map((hour, index) => ({
id: index,
temp: hour.temp,
weatherCondition: hour.weather[0].main,
weatherIcon: hour.weather[0].icon,
}));
hourlyReduced = hourlyReduced.slice(0, 24);
const dailyReduced = daily.map((day, index) => ({
id: index,
minTemp: day.temp.min,
maxTemp: day.temp.max,
weatherCondition: day.weather[0].main,
weatherIcon: day.weather[0].icon,
}));
const forecastInfo = {
utcOffset: utcOffset,
hourlyForecast: hourlyReduced,
dailyForecast: dailyReduced,
};
setForecastData(forecastInfo); // NOT WORKING
console.log(forecastData); // this is not working! it show me an empty object
console.log(hourlyReduced); // this work fine and it show the result
console.log(dailyReduced); // this work fine and it show the result
return forecastInfo;
});
};
return (
<div className="App">
<HomePage
input={input}
setInput={setInput}
city={city}
setCity={setCity}
matchesArray={matchesArray}
getCurrentWeather={getCurrentWeather}
setMatchesArray={setMatchesArray}
getWeeklyWeather={getWeeklyWeather}
locationData={locationData}
forecastData={forecastData}
/>
<MainPage locationData={locationData} forecastData={forecastData} />
</div>
);
};
export default App;
useState hooks are asynchronous. Logging forecastData right after calling setForecastData will not guarantee that the hook has finished updating the state. Use a useEffect hook to log forecastData whenever it changes.
useEffect(() => {
console.log(forecastData)
}, [forecastData])

How to hide a form after submit in react, currently you have to click a toggle button

Currently I am rendering a list of songs where there is a toggle button I made to render a form to add a song. How can I make it so when that form is submitted it will hide the form without a button click. I attempted to make a useEffect to trigger the function but I couldn't crack it. Thanks in advance.
The list of songs
import { useEffect, useState } from 'react';
import { useDispatch, useSelector } from 'react-redux';
import { deleteSong, getSongs, updateSong } from '../../store/song';
import ReactAudioPlayer from 'react-audio-player';
import { useHistory } from 'react-router';
import SongForm from '../AddSongForm';
import EditSongForm from '../EditSongForm';
import SpecificSong from '../SpecificSong';
const SongList = () => {
const [addShowForm, setAddShowForm] = useState(false);
// const [editShowForm, setEditShowForm] = useState(false);
const history = useHistory()
const dispatch = useDispatch();
const songsObj = useSelector((state) => state.songState.entries);
const songs = Object.values(songsObj)
const user = useSelector((state) => state.session.user);
const CurrentUserId = user?.id
const remove = (e) => {
dispatch(deleteSong(e.target.id));
}
const addFormCheck = (e) => {
if (addShowForm) setAddShowForm(false)
if (!addShowForm) setAddShowForm(true)
}
// const editFormCheck = (e) => {
// if (editShowForm) setEditShowForm(false)
// if (!editShowForm) setEditShowForm(true)
// }
useEffect(() => {
dispatch(getSongs());
}, [dispatch]);
return (
<div>
<div>
<button onClick={addFormCheck}>add a song</button>
{addShowForm ?
<SongForm />
: null}
</div>
<h1>Song List</h1>
<ol>
{songs.map(({ id, songName, songLink, userId }) => (
<div>
<SpecificSong id={id} songName={songName} songLink={songLink} userId={userId} />
</div>
))}
</ol>
</div>
);
};
export default SongList;
And the component that is being rendered
import { useState } from "react";
import { useDispatch } from "react-redux";
import { postSong } from "../../store/song";
import { useSelector } from "react-redux";
import Axios from 'axios'
const SongForm = () => {
const dispatch = useDispatch();
const [songName, setSongName] = useState("");
const [songLink, setSongLink] = useState("");
const [errors, setErrors] = useState([]);
const [songSelected, setSongSelected] = useState("")
const reset = () => {
setSongName("");
setSongLink("");
};
const user = useSelector((state) => state.session.user);
const userId = user?.id
let url;
const handleSubmit = async (e) => {
e.preventDefault();
const formData = new FormData()
formData.append('file', songSelected)
formData.append('upload_preset', 'd3gthd7l')
if (songSelected === '') {
setErrors(['You have to upload an audio file!'])
}
Axios.post("https://api.cloudinary.com/v1_1/dyhfkvy6u/video/upload", formData).then(async (response) => {
if (response.data.url) url = response.data.url
const newSong = {
songName,
songLink: url,
userId
};
const song = await dispatch(postSong(newSong))
.catch(async (res) => {
const data = await res.json()
if (data && data.errors) setErrors(data.errors)
})
})
// reset();
};
return (
<div className="inputBox">
<h1>Add A Song</h1>
<ul>
{errors.map((error, idx) => <li className='errors' key={idx}>{error}</li>)}
</ul>
<form onSubmit={handleSubmit}>
<input
type="text"
onChange={(e) => setSongName(e.target.value)}
value={songName}
placeholder="Song Name"
name="Song Name"
/>
<input
type='file'
onChange={(e) => { setSongSelected(e.target.files[0]) }}
placeholder="Song Link"
name="Audio File"
/>
<button type="submit">Submit</button>
</form>
</div>
);
};
export default SongForm;
You could pass the setAddShowForm function to the form as a prop and update its state once submitted (Note that you can use && for conditional rendering):
<div>
<button onClick={addFormCheck}>add a song</button>
{addShowForm && <SongForm setAddShowForm={setAddShowForm}/>}
</div>
Change your component declaration to
const SongForm = ({ setAddShowForm }) => {
And update the state in the handleSubmit method:
const handleSubmit = async (e) => {
e.preventDefault();
const formData = new FormData();
formData.append('file', songSelected);
formData.append('upload_preset', 'd3gthd7l');
if (songSelected === '') {
setErrors(['You have to upload an audio file!']);
}
Axios.post(
'https://api.cloudinary.com/v1_1/dyhfkvy6u/video/upload',
formData
).then(async (response) => {
if (response.data.url) url = response.data.url;
const newSong = {
songName,
songLink: url,
userId,
};
const song = await dispatch(postSong(newSong)).catch(async (res) => {
const data = await res.json();
if (data && data.errors) setErrors(data.errors);
// Hide the form
setAddShowForm(false);
});
});
};
You cant trigger useEffect with a dispatch variable change. Dispatch is a hook and dont change once invoked. You need to create a state variable useState and change its value on handleChange, when you do that, include that variable on useEffect instead of dispatch and that will trigger the useEffect content.

Rendering probleme while rendering the data fetched from the backend in a component

I have a problem with the re-rendering error!
import React , {useEffect} from 'react'
import "./UsernameStory.css";
import Axios from "axios";
import Storyfeed from '../storySmall/Storyfeed';
const UsernameStory = ({match}) => {
const [statue , setStatue] = React.useState("");
const [storie , setStorie] = React.useState([]);
useEffect(() => {
const urls = match.params.username;
Axios.post("http://localhost:8080/user/"+urls, {})
.then((response) => {
if(response.data.statue)
{
setStatue(response.data.statue);
}
if(response.data){
setStorie(storie.concat(response.data));
}
});
}, []); // An empty denpendency array allows you to fetch the data once on mount
return (
<div>
<p>{statue}</p>
{storie.map((story , key) => (<Storyfeed key = {key} story = {story}/>))}
</div>
)
}
export default UsernameStory
Storyfeed.js
import React , {useEffect}from 'react'
import Axios from "axios";
import {Link} from "react-router-dom";
import NotFound from '../../errors/404/NotFound';
import ThumbUpIcon from '#material-ui/icons/ThumbUp';
const Storyfeed = ({story}) => {
const [votes , setVotes] = React.useState(story.vote);
const [link , setLink] = React.useState("");
setLink(story.link)
let url = `/story/${link}`;
return(
<div className = "story">
<p>{story.username}</p>
<Link to={url}><p>{story.title}</p></Link>
<div className = "votes">
<ThumbUpIcon />
<p> Total Votes : {votes}</p>
</div>
</div>
);
}
this is the response
response.data = [{
email: "mouadpro3#gmail.com",
id: 7,
link: "hello-world",
story: "test",
title: "hello world",
username: "MH15O",
votes: 0
},
...
I have just fetched some data from my backend and used a useState to store the result array which is going to be mapped in order to show some each story data with the Storyfeed component
this is the response too , it gives an array of objects, and i wanna map them so that i can show them
I believe you're trying not to disturb the previous value of the state by attempting a concat on the previous state value. Try the below
response.data = [{
email: "mouadpro3#gmail.com",
id: 7,
link: "hello-world",
story: "test",
title: "hello world",
username: "MH15O",
votes: 0
},
{...},
{...}];
For this response given by you, I've updated the answer
const UsernameStory = ({match}) => {
const [statue, setStatue] = React.useState("");
const [story, setStory] = React.useState([]);
const setMyStory = (sto) => setStory([...story, sto]);
or
const setMyStory = (sto) => setStory(prevState => [...prevState, sto]);
useEffect(() => {
const urls = match.params.username;
Axios.post("http://localhost:8080/user/"+urls, {})
.then((response) => {
if( response.data && response.data[0].statue){
setStatue(response.data[0].statue);
} else if(response.data && response.data[0].story) {
setMyStory(response.data[0])
}
});
}, []);
return (
<div>
<p>{statue}</p>
{story.map((story , key) => (<Storyfeed key = {key} story = {story}/>))}
</div>
)
}
export default UsernameStory

Categories

Resources