Why I can't get categories? - javascript

Does anyone know why I can't filter out the categories?
Error: Objects are not valid as a React child (found: object with keys {_key, _ref, _type}). If you meant to render a collection of children, use an array instead.
I also tried with {categories.title}
when implementing
{categories && categories.map((index, category) => (
<p key={index}>{category}</p>
))}
it just shows 0 in all cards.
import React from 'react'
import Link from 'next/link'
import groq from 'groq'
import sanityClient from '../client'
import imageUrlBuilder from '#sanity/image-url';
function urlFor (source) {
return imageUrlBuilder(sanityClient).image(source)
}
const stories = ({posts}) => {
return (
<div className='bg-gray-100'>
<div className='md:grid md:grid-cols-3 px-4 py-4'>
{posts.length > 0 && posts.map(
({ _id, title = '', slug = '', description, mainImage, categories }) =>
slug && (
<div key={_id} className='py-2 md:px-2'>
<Link href="/post/[slug]" as={`/post/${slug.current}`}>
<div className='border rounded-md p-4 bg-white cursor-pointer'>
<img className='w-full' src={urlFor(mainImage).url()}
width='500'
height='500'
alt="Mainn Image"/>
<h2 className='text-2xl py-6'>{title}</h2>
<p>{categories}</p>
<p className='opacity-60'>{description}</p>
</div>
</Link>
</div>
)
)}
</div>
</div>
)
}
export async function getStaticProps() {
const posts = await sanityClient.fetch(groq`
*[_type == "post" && publishedAt < now()] | order(publishedAt desc)
`)
return {
props: {
posts
}
}
}
export default stories

The main problem is in your code is related to built-in map method of Array constructor. map 's callback function as a first parameter gets an array value and as a second an index of that array item. So you mixed up the order of callback function parameters.
Also you used a category value as a children for JSX element (in your case it's <p>), whose type is probably an object.
For example, if your category looks like this: {id: 0, title: 'Books'}, then you can use as a child or an innerHTML/innerText of <p> like this:
<p key={category.id}>{category.title}</p>,
but not like:
<p key={index}>{category}</p>

Related

I keep getting 'Cannot destructure property 'title' of 'product' as it is undefined.' in React

I am working on a react project and trying to show more details on a product with react-router useParams, but I keep getting the error 'Cannot destructure property 'title' of 'product' as it is undefined.
Here's the code for the single products
import React from "react";
import products from "../data2";
import { Link, useParams } from "react-router-dom";
const SingleProducts = () => {
const { productId } = useParams();
const product = products.find((productg) => productg.id === productId);
const { title } = product;
return (
<section className="section product">
<img />
<h3>{title}</h3>
<h3></h3>
<Link to="/products">Back home</Link>
</section>
);
};
export default SingleProducts;
and here's for the product items
import React from 'react'
import { products } from '../data2'
import style from '../Styles/ProductItem.module.scss'
import StarRateIcon from '#mui/icons-material/StarRate';
import {Link} from "react-router-dom"
const ProductItem = () => {
return (
<div className={style.container}>
{products.map((item)=>{
return (
<article key={item.id} className={style.product}>
<h1>{item.title}</h1>
<h1>{item.id}</h1>
<div>
<img src={item.images} />
</div>
<div>
<p>{item.title}</p>
<h4>${item.price}</h4>
<p>{item.stock} items left</p>
<div className={style.rating}>
<p>{item.rating}</p>
<StarRateIcon className={style.star}/>
</div>
</div>
<Link to={`/products/${item.id}`}><button className={style.btn}>View more</button></Link>
<div className={style.discount}>
<p>- {item.discountPercentage} %</p>
</div>
</article>);
})}
</div>
)
}
export default ProductItem
Array.prototype.find potentially returns undefined if no match is found. The component logic should handle this case and only attempt to access into defined objects. Add a check on the returned product value and conditionally render alternative UI.
You should also be aware that the route path params will always be a string type, so you'll want to ensure you are using a type-safe comparison in the find predicate function. Converting the value you are comparing against the path param to a string is sufficient.
Example:
const SingleProducts = () => {
const { productId } = useParams();
const product = products.find((productg) => String(productg.id) === productId);
if (!product) {
return "No matching product found.";
}
const { title } = product;
return (
<section className="section product">
<img />
<h3>{title}</h3>
<h3></h3>
<Link to="/products">Back home</Link>
</section>
);
};

Objects are not valid as a React child (found: object with keys {Dname, recipe_diet}). If you meant to render a collection of children, use an array

I can get all my api info, but when create a new "recipe" and I try to show it on the Front, I get that error, but just on the "recipe" that I created, :c
import React from "react";
import './estilos/Cartita.css'
export default function Card({image, title, diets, healthScore, createdInBd, id}){
return (
<div className="containerr">
<div className="card">
<h2>{id}</h2>
<img src={image} alt="img not found" width="200px" height="250px"/>
<h2>NOMBRE:</h2>
<h5>{title}</h5>
<p>Tipo de dieta</p>
<div >
{
(!diets.length)?
<span>This Recipe doesnt have a diet
</span> :
diets.map((diet) => (
<span><li key={diets}> {diet}</li></span>
)
)}
</div>
<h2>HEALTSCORE:</h2>
<h5>{healthScore}</h5>
<h5>{createdInBd}</h5>
</div>
</div>
);
}
and this is the Api info, the top one is from the api and the other one is the one that I created:
enter image description here
due to your screenshot diets can be array of strings or array of objects, therefore you need to check if diet is an object
{!diets.length ?
<span>This Recipe doesnt have a diet</span> :
diets.map((diet) => (
<span><li key={diet.id}>{typeof diet === 'string' ? diet : diet.Dname}</li></span>
)
)}

Encountered two children with the same key, `1`. Keys should be unique so that components maintain their identity across updates. Laravel React JS

The application works completely, but the console returns this
The complete error is: Encountered two children with the same key, 1. 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.
My backend laravel returning:
return $customers = DB :: table ('customers')
-> rightJoin ('debts', 'customers.id', '=', 'debts.customer')
-> join ('companies', 'companies.id', '=', 'debts.company')
-> join ('addresses', 'addresses.id', '=', 'customers.address')
-> join ('cities', 'cities.id', '=', 'addresses.city')
-> join ('states', 'states.id', '=', 'cities.state')
-> select ('customers. *', 'debts.debt', 'companies.company', 'addresses.streetName', 'addresses.buildingNumber', 'cities.city', 'states.uf')
-> get ();
My frontend ReactJs:
import axios from 'axios'
import React, { Component } from 'react'
import { Redirect } from 'react-router';
import { Link } from 'react-router-dom'
class CustomersList extends Component {
constructor (props) {
super(props)
this.state = {
customers: []
}
}
componentDidMount () {
axios.get('/api/customers').then(response => {
this.setState({
customers: response.data
})
})
}
render () {
const { customers } = this.state
customers.sort((a,b) => (a.name > b.name) ? 1 : ((b.name > a.name) ? -1 : 0))
console.log(customers)
return (
<div className='container py-4'>
<div className='row justify-content-center'>
<div className='col-md-8'>
<div className='card'>
<div className='card-header'>
<h4 className='list-inline-item'>All customers</h4>
</div>
<div className='card-body'>
<ul className='list-group list-group-flush'>
{customers.map(customer => (
<Link
className='list-group-item list-group-item-action d-flex justify-content-between align-items-center'
to={`/customer/${customer.id}`}
key={customer.id}
>
{customer.name}
</Link>
))}
</ul>
</div>
</div>
</div>
</div>
</div>
)
}
}
export default CustomersList ```
React Keys
Keys help React identify which items have changed, are added, or are removed. Keys should be given to the elements inside the array to give the elements a stable identity:
When you don’t have stable IDs for rendered items, you may use the item index as a key as a last resort.
For more clarification check https://reactjs.org/docs/lists-and-keys.html
change this code
{customers.map((customer,index) => (
<Link
className='list-group-item list-group-item-action d-flex justify-content-between align-items-center'
to={`/customer/${customer.id}`}
key={index}
>
{customer.name}
</Link>
))}
May be your customer id is duplicating that's why you are getting warning.
In react when you map an array of elements it expects the each element to have a unique key which it uses to identify(and further diff etc) the elements so the error is because you have two elements whose key is turning out to be 1 somehow. so check why two customer elements has same customer.id. you can read more about why/how react uses keys in-depth here: https://reactjs.org/docs/reconciliation.html#recursing-on-children

How to fix 'Objects are not valid as a React child'

Codesandbox link.
I'm getting this error when trying to use filter() through a big array of objects (defined as 'filteredCharacters'), and render only those match the id of '6' to the screen (only one does).
I console.log(filteredCharacters), and I can clearly see in console that it works. But for some reason, I'm getting a "Objects are not valid as a React child" error thrown.
The code below is from /components/content.js, in the Codesandbox link above.
import React, { Component } from 'react';
import Intro from '../intro/intro';
class Content extends Component {
render() {
// Grab the 'characters' object from App.js, and assign it to 'this.props'
const { characters } = this.props;
// Filter the chracters and return only whose 'id' belongs to that of '6'
const filteredCharacters = characters.filter(characters => {
if (characters.id === 6) {
return (
<div className="characters" key={characters.id}>
<p>Name: {characters.Name}</p>
<p>ID: {characters.id}</p>
<p>Job: {characters.Job}</p>
<p>Age: {characters.Age}</p>
<p>Weapon: {characters.Weapon}</p>
<p>Height: {characters.Height}</p>
<p>Birthdate: {characters.Birthdate}</p>
<p>Birthplace: {characters.Birthplace}</p>
<p>Bloodtype: {characters.Bloodtype}</p>
<p>Description: {characters.Description}</p>
</div>
)
}
});
// Check to see if it logs properly (it does)
console.log(filteredCharacters);
// When trying to render this to the screen below, it doesn't work
return (
<div>
{filteredCharacters}
</div>
)
}
}
export default Content;
filter will only create a new array with all the elements that returned a truthy value from the function.
You can instead use filter first to get the relevant characters, and then use map on the new array to get the JSX you want to render.
const filteredCharacters = characters
.filter(character => character.id === 6)
.map(character => (
<div className="characters" key={character.id}>
<p>Name: {character.Name}</p>
<p>ID: {character.id}</p>
<p>Job: {character.Job}</p>
<p>Age: {character.Age}</p>
<p>Weapon: {character.Weapon}</p>
<p>Height: {character.Height}</p>
<p>Birthdate: {character.Birthdate}</p>
<p>Birthplace: {character.Birthplace}</p>
<p>Bloodtype: {character.Bloodtype}</p>
<p>Description: {character.Description}</p>
</div>
));
Adding to #Tholle's answer, you could combine those operations into one with reduce
const filteredCharacters = characters
.reduce((acc, character) => {
if (character.id !== 6) return acc;
acc.push(<div className="characters" key={character.id}>
<p>Name: {character.Name}</p>
<p>ID: {character.id}</p>
<p>Job: {character.Job}</p>
<p>Age: {character.Age}</p>
<p>Weapon: {character.Weapon}</p>
<p>Height: {character.Height}</p>
<p>Birthdate: {character.Birthdate}</p>
<p>Birthplace: {character.Birthplace}</p>
<p>Bloodtype: {character.Bloodtype}</p>
<p>Description: {character.Description}</p>
</div>);
return acc;
}, []);
Currently you are using one simple object to behave as a node in the HTML structure of the component you are writing. One of the best practices to use react in such cases is to create and then call this as a react component itself.
Following is your code that now has a separate component that can be called on need:
import React, { Component } from 'react';
import Intro from '../intro/intro';
const FilteredCharcters = characters => {
characters.filter(character => {
if (character.id === 6) {
return (
<div className="characters" key={character.id}>
<p>Name: {character.Name}</p>
<p>ID: {character.id}</p>
<p>Job: {character.Job}</p>
<p>Age: {character.Age}</p>
<p>Weapon: {character.Weapon}</p>
<p>Height: {character.Height}</p>
<p>Birthdate: {character.Birthdate}</p>
<p>Birthplace: {character.Birthplace}</p>
<p>Bloodtype: {character.Bloodtype}</p>
<p>Description: {character.Description}</p>
</div>
)
}
});
class Content extends Component {
render() {
const { characters } = this.props;
return (
<div>
<FilteredCharacters characters={characters} />
</div>
)
}
}
export default Content;

Lodash to sort data in react

I am trying to order by events based on their date. I have a button that will render the events in ascending order and another button will render the events in descending order. I am using lodash's orderBy method. I am defining the orderDir in the state of my container component. The orderDir changes when the buttons get clicked. The orderBy should be the 'start' property of my data object. I believe I can get to access it. Here is my code:
import React, { Component } from 'react';
import logo from './logo.png';
import './App.css';
import EventCard from './EventCard';
import SampleData from './data/sample-data.json';
import _ from 'lodash';
let dataRaw = SampleData.data.eventSearch.edges;
let data= dataRaw;
class App extends Component {
constructor(props) {
super(props);
this.state = {
data: {data},
defaultSortIndexes: [],
orderDir:'desc'
};
this.sortEevent = this.sortEevent.bind(this);
}
sortEevent(e, type){
if (type === 'asc'){
this.setState({ orderDir:'asc'});
}else{
this.setState({ orderDir:'desc'});
}
}
render() {
let filteredEvents = this.state.data.data;
let orderDir = this.state.orderDir;
console.log(filteredEvents);
filteredEvents = _.orderBy(filteredEvents, (event)=>{
return event['start'];
}, orderDir);//order by
let events = filteredEvents.map((event, i) =>
<EventCard
key={i}
name={event.node.title}
image={event.node.painting.images[0].thumb_url}
venue={event.node.venue.name}
tickets={event.node.tickets_available}
distance={event.distance}
date={event.node.start}
firstName={event.node.artist.first_name}
lastName={event.node.artist.last_name}
artistImage={event.node.artist.images[0].thumb_url}
/>
);//map
console.log(this.state.data)
return (
<div className="App">
<div className="header-wrapper">
<div className="logo-header">
<div className="logo-wrapper">
<img src={logo} className="App-logo" alt="logo" />
</div>
<div className="menu-wrapper">
<a>Events</a>
</div>
</div>
<div className="filters-wrapper">
<div className="filters">
<p>Search Filters:</p>
<button onClick={(e) => this.sortEevent(e, 'asc')} className="green">SORT ASCENDING</button>
<button onClick={(e) => this.sortEevent(e, 'desc')}>SORT DESCENDING</button>
</div>
</div>
</div>
<div className="EventList">
{events}
</div>
</div>
);
}
}
export default App;
I am console logging the filteredEvents, which is the object that I am trying to sort. Here you can see where the 'start' property is.
Thanks so much in advance!
The value of the start element in your object is a string, and not a Date object, so the comparison will be done using strings comparison and not dates.
You can convert the strings to date using:
new Date(event.node.start)
here is the orderBy usage:
filteredEvents = _.orderBy(filteredEvents, (event)=>{
return new Date(event.node.start);
}, orderDir);//order by

Categories

Resources