Map function with Graphql, React - reactjs

I'm writing a small blog app with Gatsby (React and Graphql). There's a small thing that I don't quite understand. Here is my blog page code:
const Blog = ({ data }) => {
console.log(data)
return (
<Layout>
<h1>Blog</h1>
{data.allMarkdownRemark.edges.map(({ node }) => (
<div key={node.id}>
<h3>{node.frontmatter.title}</h3>
</div>
))}
</Layout>)
}
export default props => (
<StaticQuery
query={graphql`
query {
allMarkdownRemark {
totalCount
edges {
node {
id
frontmatter {
title
path
date
tags
}
}
}
}
}
`}
render={ data => <Blog data={data} {...props} />}
/>
)
What I'm confused by is this line:
{data.allMarkdownRemark.edges.map(({ node }) => (
my question is, why does this return an error:
{data.allMarkdownRemark.edges.map( node => (
Why are the curly brackets needed? The confusion comes from the fact that we're already receiving the list from markdownremark.edges, so why do we have to specify that it's dynamically generated content again? Is it a graphql thing?
It would be great to know the answer, thanks in advance.

This is Object Destructuring and Property Shorthand feature.
1.) What will happen when using curly braces..
-> when you iterate using map on edges list you get the edge object in your map function, at the same time you have explicitly mention to destruct node property from edge object.
{data.allMarkdownRemark.edges.map(({ node }) => (
<div key={node.id}>
<h3>{node.frontmatter.title}</h3>
</div>
))}
2.) What will happen when curly braces not use.
-> when you iterate through map on edges list you will get edge object as a parameter inside the function. in this case, you have to access node property explicitly.
{data.allMarkdownRemark.edges.map( edge => (
<div key={edge.node.id}>
<h3>{edge.node.frontmatter.title}</h3>
</div>
))}
Ref : Destructing Feature
Hope so it will clear your doubt..

It is Javascript Destructuring Feature...
let test = [{name : 'sathish'},{name : 'steve'}];
//here you are access directly with destructuring es6 feture
test.map(({name})=>{console.log(name)}
test.map((fullObj)=>{console.log(fullObj.name)})
Refere this -> js destructuring...

If I understand well, then you do not know why curly braces is needed around the node. It is an es6 syntax called destructuring. When the function parameter has a property you can extract that property in this way.
({ node }) => ( <div key={node.id}> // you can use the node here
It is equivalent with.
(data) => ( <div key={data.node.id}> // you must use the property thought the parameter
You can read more about it here: http://es6-features.org/#ObjectMatchingShorthandNotation

Related

Making .map inside .map [duplicate]

In my component's render function I have:
render() {
const items = ['EN', 'IT', 'FR', 'GR', 'RU'].map((item) => {
return (<li onClick={this.onItemClick.bind(this, item)} key={item}>{item}</li>);
});
return (
<div>
...
<ul>
{items}
</ul>
...
</div>
);
}
everything renders fine, however when clicking the <li> element I receive the following error:
Uncaught Error: Invariant Violation: Objects are not valid as a React
child (found: object with keys {dispatchConfig, dispatchMarker,
nativeEvent, target, currentTarget, type, eventPhase, bubbles,
cancelable, timeStamp, defaultPrevented, isTrusted, view, detail,
screenX, screenY, clientX, clientY, ctrlKey, shiftKey, altKey,
metaKey, getModifierState, button, buttons, relatedTarget, pageX,
pageY, isDefaultPrevented, isPropagationStopped, _dispatchListeners,
_dispatchIDs}). If you meant to render a collection of children, use an array instead or wrap the object using createFragment(object) from
the React add-ons. Check the render method of Welcome.
If I change to this.onItemClick.bind(this, item) to (e) => onItemClick(e, item) inside the map function everything works as expected.
If someone could explain what I am doing wrong and explain why do I get this error, would be great
UPDATE 1:
onItemClick function is as follows and removing this.setState results in error disappearing.
onItemClick(e, item) {
this.setState({
lang: item,
});
}
But I cannot remove this line as I need to update state of this component
I was having this error and it turned out to be that I was unintentionally including an Object in my JSX code that I had expected to be a string value:
return (
<BreadcrumbItem href={routeString}>
{breadcrumbElement}
</BreadcrumbItem>
)
breadcrumbElement used to be a string but due to a refactor had become an Object. Unfortunately, React's error message didn't do a good job in pointing me to the line where the problem existed. I had to follow my stack trace all the way back up until I recognized the "props" being passed into a component and then I found the offending code.
You'll need to either reference a property of the object that is a string value or convert the Object to a string representation that is desirable. One option might be JSON.stringify if you actually want to see the contents of the Object.
So I got this error when trying to display the createdAt property which is a Date object. If you concatenate .toString() on the end like this, it will do the conversion and eliminate the error. Just posting this as a possible answer in case anyone else ran into the same problem:
{this.props.task.createdAt.toString()}
I just got the same error but due to a different mistake: I used double braces like:
{{count}}
to insert the value of count instead of the correct:
{count}
which the compiler presumably turned into {{count: count}}, i.e. trying to insert an Object as a React child.
Just thought I would add to this as I had the same problem today, turns out that it was because I was returning just the function, when I wrapped it in a <div> tag it started working, as below
renderGallery() {
const gallerySection = galleries.map((gallery, i) => {
return (
<div>
...
</div>
);
});
return (
{gallerySection}
);
}
The above caused the error. I fixed the problem by changing the return() section to:
return (
<div>
{gallerySection}
</div>
);
...or simply:
return gallerySection
React child(singular) should be type of primitive data type not object or it could be JSX tag(which is not in our case). Use Proptypes package in development to make sure validation happens.
Just a quick code snippet(JSX) comparision to represent you with idea :
Error : With object being passed into child
<div>
{/* item is object with user's name and its other details on it */}
{items.map((item, index) => {
return <div key={index}>
--item object invalid as react child--->>>{item}</div>;
})}
</div>
Without error : With object's property(which should be primitive, i.e. a string value or integer value) being passed into child.
<div>
{/* item is object with user's name and its other details on it */}
{items.map((item, index) => {
return <div key={index}>
--note the name property is primitive--->{item.name}</div>;
})}
</div>
TLDR; (From the source below) : Make sure all of the items you're rendering in JSX are primitives and not objects when using React. This error usually happens because a function involved in dispatching an event has been given an unexpected object type (i.e passing an object when you should be passing a string) or part of the JSX in your component is not referencing a primitive (i.e. this.props vs this.props.name).
Source - codingbismuth.com
Mine had to do with forgetting the curly braces around props being sent to a presentational component:
Before:
const TypeAheadInput = (name, options, onChange, value, error) => {
After
const TypeAheadInput = ({name, options, onChange, value, error}) => {
I too was getting this "Objects are not valid as a React child" error and for me the cause was due to calling an asynchronous function in my JSX. See below.
class App extends React.Component {
showHello = async () => {
const response = await someAPI.get("/api/endpoint");
// Even with response ignored in JSX below, this JSX is not immediately returned,
// causing "Objects are not valid as a React child" error.
return (<div>Hello!</div>);
}
render() {
return (
<div>
{this.showHello()}
</div>
);
}
}
What I learned is that asynchronous rendering is not supported in React. The React team is working on a solution as documented here.
Mine had to do with unnecessarily putting curly braces around a variable holding a HTML element inside the return statement of the render() function. This made React treat it as an object rather than an element.
render() {
let element = (
<div className="some-class">
<span>Some text</span>
</div>
);
return (
{element}
)
}
Once I removed the curly braces from the element, the error was gone, and the element was rendered correctly.
For anybody using Firebase with Android, this only breaks Android. My iOS emulation ignores it.
And as posted by Apoorv Bankey above.
Anything above Firebase V5.0.3, for Android, atm is a bust. Fix:
npm i --save firebase#5.0.3
Confirmed numerous times here
https://github.com/firebase/firebase-js-sdk/issues/871
I also have the same problem but my mistake is so stupid. I was trying to access object directly.
class App extends Component {
state = {
name:'xyz',
age:10
}
render() {
return (
<div className="App">
// this is what I am using which gives the error
<p>I am inside the {state}.</p>
//Correct Way is
<p>I am inside the {this.state.name}.</p>
</div>
);
}
}
Typically this pops up because you don't destructure properly. Take this code for example:
const Button = text => <button>{text}</button>
const SomeForm = () => (
<Button text="Save" />
)
We're declaring it with the = text => param. But really, React is expecting this to be an all-encompassing props object.
So we should really be doing something like this:
const Button = props => <button>{props.text}</button>
const SomeForm = () => (
<Button text="Save" />
)
Notice the difference? The props param here could be named anything (props is just the convention that matches the nomenclature), React is just expecting an object with keys and vals.
With object destructuring you can do, and will frequently see, something like this:
const Button = ({ text }) => <button>{text}</button>
const SomeForm = () => (
<Button text="Save" />
)
...which works.
Chances are, anyone stumbling upon this just accidentally declared their component's props param without destructuring.
Just remove the curly braces in the return statement.
Before:
render() {
var rows = this.props.products.map(product => <tr key={product.id}><td>{product.name}</td><td>{product.price}</td></tr>);
return {rows}; // unnecessary
}
After:
render() {
var rows = this.props.products.map(product => <tr key={product.id}><td>{product.name}</td><td>{product.price}</td></tr>);
return rows; // add this
}
I had the same problem because I didn't put the props in the curly braces.
export default function Hero(children, hero ) {
return (
<header className={hero}>
{children}
</header>
);
}
So if your code is similar to the above one then you will get this error.
To resolve this just put curly braces around the props.
export default function Hero({ children, hero }) {
return (
<header className={hero}>
{children}
</header>
);
}
I got the same error, I changed this
export default withAlert(Alerts)
to this
export default withAlert()(Alerts).
In older versions the former code was ok , but in later versions it throws an error. So use the later code to avoid the errror.
This was my code:
class App extends Component {
constructor(props){
super(props)
this.state = {
value: null,
getDatacall : null
}
this.getData = this.getData.bind(this)
}
getData() {
// if (this.state.getDatacall === false) {
sleep(4000)
returnData("what is the time").then(value => this.setState({value, getDatacall:true}))
// }
}
componentDidMount() {
sleep(4000)
this.getData()
}
render() {
this.getData()
sleep(4000)
console.log(this.state.value)
return (
<p> { this.state.value } </p>
)
}
}
and I was running into this error. I had to change it to
render() {
this.getData()
sleep(4000)
console.log(this.state.value)
return (
<p> { JSON.stringify(this.state.value) } </p>
)
}
Hope this helps someone!
If for some reason you imported firebase. Then try running npm i --save firebase#5.0.3. This is because firebase break react-native, so running this will fix it.
In my case it was i forgot to return a html element frm the render function and i was returning an object . What i did was i just wrapped the {items} with a html element - a simple div like below
<ul>{items}</ul>
Just remove the async keyword in the component.
const Register = () => {
No issues after this.
In my case, I added a async to my child function component and encountered this error. Don't use async with child component.
I got this error any time I was calling async on a renderItem function in my FlatList.
I had to create a new function to set my Firestore collection to my state before calling said state data inside my FlatList.
My case is quite common when using reduce but it was not shared here so I posted it.
Normally, if your array looks like this:
[{ value: 1}, {value: 2}]
And you want to render the sum of value in this array. JSX code looks like this
<div>{array.reduce((acc, curr) => acc.value + curr.value)}</div>
The problem happens when your array has only one item, eg: [{value: 1}].
(Typically, this happens when your array is the response from server so you can not guarantee numbers of items in that array)
The reduce function returns the element itself when array has only one element, in this case it is {value: 1} (an object), it causes the Invariant Violation: Objects are not valid as a React child error.
You were just using the keys of object, instead of the whole object!
More details can be found here: https://github.com/gildata/RAIO/issues/48
import React, { Component } from 'react';
import PropTypes from 'prop-types';
class SCT extends Component {
constructor(props, context) {
super(props, context);
this.state = {
data: this.props.data,
new_data: {}
};
}
componentDidMount() {
let new_data = this.state.data;
console.log(`new_data`, new_data);
this.setState(
{
new_data: Object.assign({}, new_data)
}
)
}
render() {
return (
<div>
this.state.data = {JSON.stringify(this.state.data)}
<hr/>
<div style={{color: 'red'}}>
{this.state.new_data.name}<br />
{this.state.new_data.description}<br />
{this.state.new_data.dependtables}<br />
</div>
</div>
);
}
}
SCT.propTypes = {
test: PropTypes.string,
data: PropTypes.object.isRequired
};
export {SCT};
export default SCT;
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react-dom.min.js"></script>
If you are using Firebase and seeing this error, it's worth to check if you're importing it right. As of version 5.0.4 you have to import it like this:
import firebase from '#firebase/app'
import '#firebase/auth';
import '#firebase/database';
import '#firebase/storage';
Yes, I know. I lost 45 minutes on this, too.
I just put myself through a really silly version of this error, which I may as well share here for posterity.
I had some JSX like this:
...
{
...
<Foo />
...
}
...
I needed to comment this out to debug something. I used the keyboard shortcut in my IDE, which resulted in this:
...
{
...
{ /* <Foo /> */ }
...
}
...
Which is, of course, invalid -- objects are not valid as react children!
I'd like to add another solution to this list.
Specs:
"react": "^16.2.0",
"react-dom": "^16.2.0",
"react-redux": "^5.0.6",
"react-scripts": "^1.0.17",
"redux": "^3.7.2"
I encountered the same error:
Uncaught Error: Objects are not valid as a React child (found: object
with keys {XXXXX}). If you meant to render a collection of children,
use an array instead.
This was my code:
let payload = {
guess: this.userInput.value
};
this.props.dispatch(checkAnswer(payload));
Solution:
// let payload = {
// guess: this.userInput.value
// };
this.props.dispatch(checkAnswer(this.userInput.value));
The problem was occurring because the payload was sending the item as an object. When I removed the payload variable and put the userInput value into the dispatch everything started working as expected.
If in case your using Firebase any of the files within your project.
Then just place that import firebase statement at the end!!
I know this sounds crazy but try it!!
I have the same issue, in my case,
I update the redux state, and new data parameters did not match old parameters, So when I want to access some parameters it through this Error,
Maybe this experience help someone
My issue was simple when i faced the following error:
objects are not valid as a react child (found object with keys {...}
was just that I was passing an object with keys specified in the error while trying to render the object directly in a component using {object} expecting it to be a string
object: {
key1: "key1",
key2: "key2"
}
while rendering on a React Component, I used something like below
render() {
return this.props.object;
}
but it should have been
render() {
return this.props.object.key1;
}
If using stateless components, follow this kind of format:
const Header = ({pageTitle}) => (
<h1>{pageTitle}</h1>
);
export {Header};
This seemed to work for me
Something like this has just happened to me...
I wrote:
{response.isDisplayOptions &&
{element}
}
Placing it inside a div fixed it:
{response.isDisplayOptions &&
<div>
{element}
</div>
}

Registering nested objects with React Hook Form

I have been working with RHF for a while and it helps a lot actually, but I have been trying to do something for what I have not enough knowledge maybe.
So the thing its that I have a nested object that I bring to my componen throw props
const Child = ({ globalObject, register }) => {
const renderNested = Object.entries(globalObject.nestedObject);
return (
<span>
{renderNested.map(([key, value], index) => {
return (
<div key={index}>
<Field
type="text"
label={key}
name{`nestedObject.${key}`}
defaultValue={value}
ref={register}
/>
</div>
);
})}
</span>
);
};
All good, now, one of the keys inside this nestedObject has another object as a value, for which when I map over them and display, the field will show: [object Object]
I know how I would solve that issue if I am using a useState, for example.
Not sure if its a good practice but I would go with something like:
defaultValue={typeof value === 'someKey' ? value[key] : value}
but in this case using register (which I want to use since it saved me from other nightmares) I am not sure how to solve this.
Should I filter the array outside and then render for one side the keys that dont have an object as a value and then the rest?
It looks to me like it has to be something better than that.
Can anyone give advices?
just to clarify, nestedObject looks like:
nestedObject: {
key: value
key: {key:value}
}
You can access the fields using . dot notation, as per documentation for the register method. So register("name.key") works to retrieve the nested object as well as arrays, however note that in React Hook Form v7 the syntax to retrieve nested array items changed from arrayName[0] to arrayName.0.
Your component would look similar to the following:
const Child = ({ globalObject, register }) => {
const nestedKeys = Object.keys(globalObject.nestedObject);
return (
<>
{nestedKeys.map((key) => (
<Field key={key} {...register(`nestedObject.${key}`)} />
))}
</>
);
};
You should not use index as key prop in this case, as you have another stable identifier for each array element which comes from unique nested object keys.

Mapping through an Array of Objects doesn't work in React

I want to insert a key identifier in a React App. Currently, my key is being populated by the Object.name property. That is not ideal, as it could be duplicated and then React would start complaining. The server doesn't return such a key.
So, I thought, I would map through that array and use the index in my key parameter. So, this is what I did:
const item = itemsList.map((item, index) => ({
...item,
index
}));
And used it here in my component:
<TableWithSearch
keyField="item.index"
data={itemsList}
columns={getItemsTableColumns()}
search
>
I think this is straightforward enough to understand. But then my app crashes. With the error:
Warning: Each child in an array or iterator should have a unique "key" prop..
Obviously I am doing something wrong, but I am looking at it, for the past 1 hour and still can't figure it out.
keyField="item.index" is incorrect. Your are passing it as a string which is why you are seeing that error. Each element is getting the same string value of "item.index"
It should be keyField={item.index}
Ok, after a few hours of thinking about your comments and answers, and some experimenting, I found a solution.
For the sake of explaining a solution more thoroughly I will post my entire component:
export class ItemsList extends Component {
static propTypes = {
listItems: PropTypes.func.isRequired
};
componentDidMount() {
const { listItems } = this.props;
listItems();
}
renderGroups() {
const { itemsList, user } = this.props;
const items = itemsList.map((item, index) => ({
...item,
index
}));
return (
<Fragment>
{itemsList.length > 0 ? (
<Fragment>
<PageHeader>
Groups
<span>({itemsList.length})</span>
</PageHeader>
<div className="mb-2">
<TableWithSearch
keyField="index"
data={items}
columns={getItemsTableColumns()}
search
>
</TableWithSearch>
</div>
</Fragment>
) : (
<div>no items</div>
)}
</Fragment>
);
}
My mistake was that I was passing it ListGroups as a data attribute, which in return made it quite impossible for the component to read the groups array and take the index of it Ids van der Zee was right in his comment.
For some reason, JSX syntax on the key attribute didn't work as you guys suggested. I haven't investigated this, but I will and comment below.
The above example works like a charm. If you know why JSX Syntax didn't work, please feel free to comment. Thanks for your help guys..

React: how to forward refs to multiple children?

I'm trying to forward multiple refs to multiple children DOM nodes:
I need references to the 7 buttons, so I can manage focus between them.
I tried by using an array of React.createRef() and then attach each element of this array to a child using index, but all refs refers to the last button.
Why and is there another solution?
class RestaurantsList extends Component {
references = Array(7).fill(React.createRef());
render() {
return (
<ul
id="restaurants-list"
role="menu"
>
{
this.props.restaurants.map((restaurant, index) => {
return (
<Restaurant
ref={this.references[index]}
name={restaurant.name}
/>
);
})
}
</ul>
);
}
}
const Restaurant = React.forwardRef((props, ref) => {
return (
<li>
<button
ref={ref}
>
{name}
</button>
</li>
);
})
As it was discussed in comments the best way is to keep list of references as an array or object property inside parent component.
As for Array.prototype.fill() its arguments is calculated just once. In other words fill(React.createRef()) will generate list where each entry will refer to the same object - and you will get equal ref to last element. So you need to use .map() for getting unique reference objects.
references = Array(7).fill(0).map(() => React.createRef());
Anyway in real world project this will rather happen in constructor() or componentDidUpdate().
But I believe it's better to have hashmap:
references = {};
getOrCreateRef(id) {
if (!this.references.hasOwnProperty(id)) {
this.references[id] = React.createRef();
}
return this.references[id];
}
render() {
return (
....
{
this.props.restaurants.map((restaurant, index) => {
return (
<Restaurant
ref={this.getOrCreateRef(restaurant.id)}
key={restaurant.id}
name={restaurant.name}
/>
);
})
}
Also you will need some helper methods to avoid exposing this.references to outer world:
focusById(id) {
this.references[id].current && this.references[id].current.focus();
}
And take special attention to cleaning up references to unmounted elements. Otherwise you may got memory leak if list of restaurants is changed dynamically(if ref stays in this.references it keeps reference to HTML element even if it has been detached). Actual need depends on how is your component used. Also this memory leakage will be fixed once container(that has reference = {}) is unmounted itself due to navigating away.

Passing a data attribute through a stateless component to its HTML output

Say I have this List stateless component:
const List: React.StatelessComponent<ListProps> = props => {
const listClasses =
'atom-list uk-list ' +
props.className +
(props.striped ? ' uk-list-striped' : '')
return (
<ul className={listClasses}>
{props.items.map((item, index) => {
const itemClasses = [item.classes, props.childClasses]
.filter(prop => prop)
.join(' ')
return (
<li key={index} className={itemClasses}>
{item.content}
</li>
)
})}
</ul>
)
}
And I use the List component in another stateless component, SortableList:
const SortableList: React.StatelessComponent<SortableListProps> = props => {
const classes = 'molecule-sortableList ' + props.className
const items = props.items.map((item, index) => {
return { content: <div><div className="uk-sortable-handle" />{item.content}</div> }
})
return <List className={classes} items={items} data-uk-sortable="handle: .uk-sortable-handle" />
}
Because I'm using UIKit to make the list sortable, I need to apply a data attribute to the List element when I use it in SortableList, and have that data attribute output in the resulting ul.
Applying the data attribute directly to the ul in List works (the attribute is output in the resulting HTML), but that's not ideal, since it requires that List knows something irrelevant about how it's being used.
I really want to apply the data attribute directly to List's usage and have that output in the resulting ul. Unfortunately, doing something like this in SortableList doesn't seem to work:
return <List className={classes} items={items} data-uk-sortable="handle: .uk-sortable-handle" />
Why doesn't this work, and what do I need to do to pass the data attribute into List and have it output in the resulting ul?
You can do this by using approach similar to the following:
const List: React.StatelessComponent<ListProps> = (props) => {
return (
<div {...props}>List</div>
);
};
The example is overly simplified and you should not destruct props directly as they contain some extra properties you do not want to be rendered as attributes. Instead you can copy all of the props properties that start for example with "data-" into separate object and destruct it instead.

Resources