React display data from object TypeError: value map is not a function - node.js

Getting "TypeError: value is undefined" error when trying to render the data from the value object, when I console log value the object does show.
CODE
import React from "react";
import { useParams } from "react-router-dom";
import {
useDocumentData,
} from "react-firebase-hooks/firestore";
import firebase from "../firebase";
const firestore = firebase.firestore();
const Threads = () => {
const { threadId } = useParams();
const [value, loading, error] = useDocumentData(firestore.collection("threads").doc(threadId));
console.log(value);
return (
<div>
{value.map((thread, i) => (
<div>
<h1>{thread.title}</h1>
<p>{thread.desc}</p>
</div>
))}
</div>
);
};
export default Threads;
CONSOLE
ERROR

The variable value is a object. Map function in JS is applicable only for arrays.
So try this.
<div>
<h1>{value.title}</h1>
<p>{value.desc}</p>
</div>

Related

reaching data inside an object while using map

I'm sorry in advance for my ignorance, But its been a while since I coded, and I need a little help with this basic thing, Im trying to reach name, and display it on screen when I click a button, but i just can't figure it out, can u help me and explain what you did so I can know for the next time? looked manuals and articles about how to do it but you guys are just simply better.
my code:
functional (pokeAPI.ts):
import React from "react";
import axios from 'axios'
export const getPokemon=()=>{
axios.get("https://pokeapi.co/api/v2/pokemon?limit=151").then((response)=>{
response.data.results.map((cont : string,index : number) => console.log(
cont
));
})
}
app.tsx:
import './App.css';
import React from 'react';
import {getPokemon} from './func/pokeAPI';
function App() {
return (
<div>
<button onClick={getPokemon}>Cli</button>
</div>
);
}
export default App;
Here you go! Since you're using typescript, I created a PokemonData interface as well.
import React from 'react'
import axios from 'axios'
import "./styles.css";
interface PokemonData {
name: string
url: string
}
export default function App() {
const [pokemons, setPokemons] = React.useState<null|PokemonData[]>(null)
const onClick = async () => {
axios.get("https://pokeapi.co/api/v2/pokemon?limit=151").then((response)=>{
console.log(response.data.results)
setPokemons(response.data.results)
})
}
return (
<div className="App">
<button onClick={onClick}>Get pokemon</button>
{pokemons?.map((pokemon) => (
<div key={pokemon.name}>
<p>{pokemon.name}</p>
<p>{pokemon.url}</p>
</div>
))}
</div>
);
}
I also wrote a Codesandbox here so you can check it out
Edit: Another version of the return statement if optional chaining isn't supported on your project's version
return (
<div className="App">
<button onClick={onClick}>Get pokemon</button>
{pokemons && pokemons.map((pokemon) => (
<div key={pokemon.name}>
<p>{pokemon.name}</p>
<p>{pokemon.url}</p>
</div>
))}
</div>
);
}

Show div when there's not data available in getServerSideProps with NextJS?

Ok, this is my third post regarding NextJS.
I'm currently making a search page which needs to fetch data from 3 endpoints according to the keyword found in the URL; if no data found, show a message in the proper call.
The current behaviour throws an error even if there's data found in 1-2 endpoints:
import { useEffect, useState, useContext } from 'react';
import { withRouter } from 'next/router';
// ACTIONS
import { getProducersFromServer } from '#/actions/producer';
import { getVideosFromServer } from '#/actions/video';
import { getProfilesFromServer } from '#/actions/profile';
// HELPERS
import Layout from '#/layout/Layout';
import { PUBLIC_URL } from '../../config';
import NothingFoundAlert from '#/layout/NothingFoundAlert';
import AuthContext from '#/routing/authContext';
// REACTBOOTSTRAP
import Row from 'react-bootstrap/Row';
import Col from 'react-bootstrap/Col';
// NESTED COMPONENTS
import SearchMenu from './SearchMenu';
import SingleProducer from '../producers/singleProducer';
import SingleVideo from '../videos/singleVideo';
import SingleProfile from '../profiles/singleProfile';
export const getServerSideProps = async (context) => {
const keyword = context.query.keyword;
const params = `?keyword=${keyword}&page=${context.query.page}&limit=${context.query.limit}&sort=${context.query.sort}&status=${context.query.status}`;
const producers = (await getProducersFromServer(params)()) || [];
const videos = (await getVideosFromServer(params)()) || [];
const params2 = `?keyword=${keyword}&page=${context.query.page}&limit=${context.query.limit}&sort=${context.query.sort}&isEmailConfirmed=true`;
const profiles = (await getProfilesFromServer(params2)()) || [];
return {
props: {
params: params,
params2: params2,
keyword: keyword,
serverProducers: producers?.data,
serverVideos: videos?.data,
serverProfiles: profiles?.data
}
};
};
const All = ({
params,
params2,
keyword,
serverProducers,
serverVideos,
serverProfiles,
router
}) => {
const [searchProducerResults, setSearchProducerResults] = useState([]);
const [searchVideoResults, setSearchVideoResults] = useState([]);
const [searchProfileResults, setSearchProfileResults] = useState([]);
useEffect(() => {
setSearchProducerResults(serverProducers);
setSearchVideoResults(serverVideos);
setSearchProfileResults(serverProfiles);
}, [params]);
const { auth } = useContext(AuthContext);
return (
<Layout
title={`Search Results of ${keyword}`}
description={`Search results`}
author={`Kevin Fonseca`}
sectionClass={`mb-3`}
containerClass={`container`}
canonical={`${PUBLIC_URL}`}
url={`search${params}`}
>
<SearchMenu params={params} params2={params2} />
<div
className={
auth?.user?.data?.settings.theme.themeContainer
? auth?.user?.data.settings.theme.themeContainer
: `container`
}
>
<Row>
<Col xl={`12`} lg={`12`}>
{searchProducerResults?.length > 0 ? (
<>
<h4 className={`my-2 mb-3`}>
Producers ({totalProducerResults})
</h4>
{searchProducerResults.map((producer, index) => (
<SingleProducer key={producer._id} producer={producer} />
))}
</>
) : (
<NothingFoundAlert />
)}
{searchVideoResults?.length > 0 ? (
<>
<hr />
<h4 className={`my-2 mb-3`}>Videos ({totalVideoResults})</h4>
<div className={`recommendedVideos_videos`}>
{searchVideoResults.map((video, index) => (
<SingleVideo key={video._id} video={video} />
))}
</div>
</>
) : (
<NothingFoundAlert />
)}
{searchProfileResults?.length > 0 ? (
<>
<hr />
<h4 className={`my-2 mb-3`}>
Profiles ({totalProfileResults})
</h4>
<div className={`profiles-container`}>
{searchProfileResults
.filter((profile) => profile._id !== auth?.user.data._id)
.map((profile, index) => (
<SingleProfile
key={profile._id}
profile={profile}
auth={auth}
/>
))}
</div>
</>
) : (
<NothingFoundAlert />
)}
</Col>
</Row>
</div>
</Layout>
);
};
export default withRouter(All);
In the code above, I'm trying to show the NothingFoundAlert component in each of the variable but it currently throws an error of:
Error: Error serializing `.serverProducers` returned from `getServerSideProps` in "/search".
Reason: `undefined` cannot be serialized as JSON. Please use `null` or omit this value.
NOTE: I'm using express
I just solved it, the solution was to declare an empty array for each variable when data was not found:
serverProducers: producers?.data || [],
serverVideos: videos?.data || [],
serverProfiles: profiles?.data || []
Still unsure why I need to create a second || [] again but this is the only way it works.

How can I rerender an array of components in react?

I'm traing to make an experiment in react and websockets. The web app sends an input from the user to a server and the server responds. For each response from the server the web creates a component to show the response. I don't know how to rerender the array after each response.
import React, { useState, Component } from 'react'
import './HomePage2.css'
import Text from '../../commons/Text/Text'
import Input from '../../commons/Input/Input'
import TextInput from '../../commons/TextInput/TextInput'
const ws = new WebSocket('ws://localhost:5656')
var path = 'path$ '
var items = []
const hc = () =>{
this.forceUpdate()
}
ws.addEventListener("message", data => {
console.log(data.data)
items.push(<TextInput path={path} text={data.data} />)
//items.push(data.data)
if(data.data !== ''){
items.push(<Text text={data.data} />)
}
})
class HomePage2 extends Component {
render(){
function setCommand(comm){
ws.send(comm)
}
return(
<div className='homepage2' >
{items}
<Input path={path} callback={setCommand} />
</div>
)
}
}
export default HomePage2
save received data in an array and use the map method to render an element for each value in the array, e.g:
return (
<>
{items.map((item, key) => (
<TextInput path={path} text={item.data} key={key} />
))}
</>
);

How to make states from the keys of state object in react?

So I was trying to implement states in Child component from the Parent component state,as you can see in the code.But it gives me undefined as state value in child componenet.To test you can conosle.log(questions) and you will see undefined.
Is there a mechanism to setState in Parent component in some way such that the subsequent props in child components wil be able to get the state values?
Here is my code:
import React, { useEffect, useState } from "react";
import io from "socket.io-client";
const ENDPOINT = "http://localhost:5000";
let socket = io(ENDPOINT);
export default function Screen() {
const [qValue, setQuestion] = useState({personalInfo:{},questions:[]});
const [aValue, setAnswer] = useState({personalInfo:{},answer:""});
useEffect(() => {
socket.on("screenAns", (input) => {
setAnswer(JSON.parse(input));
});
console.log(aValue);
}, [aValue]);
useEffect(() => {
socket.on("screenQs", (arrayValue) => {
setQuestion(JSON.parse(arrayValue));
});
console.log((qValue));
}, [qValue]);
return (
<div>
<h2>Screen</h2>
<QuestionSingleMode value={qValue} />
</div>
);
}
function QuestionSingleMode(props){
var [questions,setQuestions]=useState(props.value.questions);
var [renderQuestion,setRenderQuestion]=useState("")
var [counter,setCounter]=useState(props.value.questions.length)
useEffect(()=>{
console.log(questions)
setRenderQuestion(questions[0])
},[renderQuestion])
function nextQuestion(){
setQuestions(questions.splice(0,1))
setRenderQuestion(questions[0])
setCounter(counter--)
}
return(
<div>
<h1>{renderQuestion}</h1>
<button onClick={nextQuestion}>{counter ? "next" : "finish"}</button>
</div>
)
}
Actually I solved the issue by changing the renderQuestion to props.questions in the useEffect() array.

TypeError: Cannot read property startsWith of undefined

import React from 'react';
import AttributeDescription from './AttributeDescription';
class CompEntry extends React.Component{
render(){
let description;
if(this.props.description.startsWith("_")){
description= this.props.description.slice(1, this.props.description.length);
}
if(this.props.description.startsWith("__")){
description = this.props.description.slice(2, this.props.description.length);
}
return(
<div>
<div>
<AttributeDescription description={description}/>
</div>
</div>
);
};
}
export default CompEntry;
The mentioned error happened if I do the stuffs before the return. However, if i dont do anything before the return and just pass this props.description into the description prop of the <AttributeDescription/> tag, everything works fine, a defined props is passed into the tag. It seems like if the value of this.props.description does not exist if i try to access its property. Anyone knows why?
This is how I use the CompEntry component above:
import React from 'react';
import CompEntry from './CompEntry';
import CompHeading from './CompHeading';
class CompTable extends React.Component{
constructor(props){
super(props);
this.state = {
products: [],
attributes: [],
attDesc: [],
};
this.getEntries = this.getEntries.bind(this);
}
getEntries = async () => {
const response = await fetch('/api/hello/data');
const body = response.json();
return body;
};
componentDidMount(){
this.getEntries()
.then((resolve) => this.setState({
products: resolve.products,
attributes: resolve.attributes,
attDesc: resolve.attributesDescription}))
.catch(err=>console.log(err));
};
render(){
console.log(this.state.products);
let highlightEntry= true;
let compEntries = this.state.attributes.map( (item, index) =>{
highlightEntry = !highlightEntry;
return(
<CompEntry key={index} attribute={item} description={this.state.attDesc[index]} comparees={this.state.products} color={highlightEntry}/>
);
});
return(
<div id = "comp-table">
<div id="comp-sub-container">
<CompHeading comparees={this.state.products}/>
{compEntries}
</div>
</div>
);
}
}
export default CompTable;
Edit: As mentioned by #awarrier99 in the comments, the response.json() function returns a Promise so you need to handle that appropriately. The code below has been updated for that also.
You do not set description to anything if the leading character is not an underscore. Also if it starts with two underscores, it also starts with one underscore so that can double the work. I recommend doing this:
render(){
let description = this.props.description;
if (description.startsWith("__")) {
description = description.slice(2, description.length);
} else if (description.startsWith("_")) {
description= description.slice(1, description.length);
}
return(
<div>
<div>
<AttributeDescription description={description}/>
</div>
</div>
);
};
}
This way if this.props.description does not start with any underscores it will still send that value, and the slice only gets done once if there are underscores. The code also gets easier to read by using the simpler description variable instead of this.props.description being repeated throughout.
Update your getEntries function to return the Promise given by the json() function. You could also await on it, but since getEntries is async it's already returning a Promise so this is simplest.
getEntries = async () => {
const response = await fetch('/api/hello/data');
return response.json(); // return the Promise
};

Resources