I'm very green at react, and I'm trying to learn the library by messing around a bit.
I'm having problems passing an object to another component.
Ive created an array of objects that holds 2 string variables, 1 function and 1 int.
I'm trying to populate this inside a Grid container(Material UI framework).
When I pass the object to the other react component, it arrives as undefined. Ive tried to check if I'm passing a valid object by logging it to the console first, and it is valid.
I've also tried to refer to the properties in the component that receives the object, but the browser console throws Uncaught TypeError: Cannot read properties of undefined (reading 'person').
Does anyone know why it is being sent as undefined?
PersonList.js:
const personListe = [
{
firstName:'testFirstname',
lastName:'testLastName',
getFullName:function()
{
return `${this.firstName} ${this.lastName}`
},
age:28
}
]
export default function PersonList() {
return (
<>
<Grid container spacing={3}>
{personListe.map((person) => {
return(
<Grid item xs={3} key={person.firstName}>
<PersonCard person={person} />
</Grid>
)
})}
</Grid>
</>
);
}
PersonCard.js:
export default function PersonCard({props})
{
return (<>
{console.log(props)}
</>)
}
The PersonCard Component recieves props object which includes the person object inside it.
export default function PersonCard({person})
{
return (
<>
{console.log(person)}
</>
)
}
OR
export default function PersonCard(props)
{
return (
<>
{console.log(props.person)}
</>
)
}
Related
This question already has answers here:
JS/ES6: Destructuring of undefined
(8 answers)
Closed 1 year ago.
I have a small project, to fetch image from unsplash, even I check many times, I still have one problem in my code, I am running my code, but it is always give this error, but I don't get it. Any idea will be appreciated.
Mainboard.js
import React from 'react';
import styled from 'styled-components';
import Pin from './Pin';
function Mainboard(props) {
let { pins } = props;
return (
<Wrapper>
<Container>
{pins.map((pin, index) => {
let {urls} = pin;
return <Pin key={index} urls={urls}/>
})}
</Container>
</Wrapper>
)}
export default Mainboard;
Pin.js:
import React from 'react';
import styled from 'styled-components';
function Pin(props) {
let { urls } = props;
return (
<Wrapper>
<Container>
<img src={urls?.regular} alt="pin"/>
</Container>
</Wrapper>
)}
export default Pin;
I don't think the main issue is destructing from an undefined object (this is only a symptom), but more that you have "holes" in your data, i.e. your array has undefined elements in it.
If there is an undefined element in your pins array then perhaps you should filter them first before mapping. A quick way is to apply a Boolean constructor against the truthy defined values and the falsey undefined values, i.e. arr.filter(Boolean).
function Mainboard({ pins }) {
return (
<Wrapper>
<Container>
{pins.filter(Boolean).map((pin, index) => {
const {urls} = pin;
return <Pin key={index} urls={urls}/>
})}
</Container>
</Wrapper>
);
}
Of course you should try to ensure your data array is properly maintained in the first place so "holes" aren't introduced. If you add how Mainboard is rendered, what the props values are that are passed to it we may be better able to help optimize.
I can see item and item id as it loops to render on the screen but i don't see the value of id when i click on any of the Tile where Tile is a div and react styled component.
class CategoryOffers extends React.Component {
passidtopointscreen =(id)=>{
console.log("id is", id);
localStorage.setItem('points_id',id);
this.props.history.push('/marketplacepoints')
debugger
}
render() {
debugger
return (
<Wrapper>
{this.props &&
this.props.cards_data &&
this.props.cards_data.map(item => {
return (
<Tile onClick={(item)=>this.passidtopointscreen(item.id)}>
<ImageWrapper>
<Image src={item.logo} height={'24px'} width={'73px'} />
</ImageWrapper>
<CardString>{item.offer_summary}</CardString>
</Tile>
)
})}
</Wrapper>
)
}
}
onClick={()=>this.passidtopointscreen(item.id)}
while adding item there you create new instance for this keyword for no reason
By having the same argument-name as your already decleared argument (item), you overwrite the outer argument. There should be no reason for you here to use the event-argument, if I have understood your question correctly.
I would also suggest avoiding localstorage and instead make use of the state.
I made the component into functional one here:
import React from "react";
const CategoryOffers = ({history,cards_data}) => {
const passidtopointscreen =(id)=>{
localStorage.setItem('points_id',id);
history.push('/marketplacepoints')
}
return (
<Wrapper>
{
cards_data?.map(item => {
return (
<Tile onClick={(event)=>passidtopointscreen(item.id)}>
<ImageWrapper>
<Image src={item.logo} height={'24px'} width={'73px'} />
</ImageWrapper>
<CardString>{item.offer_summary}</CardString>
</Tile>
)
})}
</Wrapper>
)
}
}
I want to pass two functions to onClick event which is handleSubmit and handleDelete to the HomePage.js from the HomeItem.js
Here is my Error:
No duplicate props allowed react/jsx-no-duplicate-props.
Here is my HomePage.js:
const HomePage = props => {
const tvshow = props.item;
let res;
if (tvshow.length > 0) {
res = tvshow.map(res=> (
<Content item={res} onClick={props.onClick}/>
));
}
return (
<div>
<Container>
<Row>{res}</Row>
</Container>
</div>
);
};
export default HomePage;
Here is my HomeItem.js:
const HomeItem = props => {
function handleSubmit() {
props.onClick({
name: props.item.name,
id: props.item.id
});
}
function handleName() {
props.onClick({
name: props.item.name
});
}
<Button onClick={handleSubmit}></Button>
<Button onClick={handleName}></Button>
Here is my App.js:
handleSubmit(newFavorite) {}
handleName(newFavorite) {}
render() {
<Route
exact
path="/"
render={() => (
<HomePage
item={this.state.SaveFavorite}
onClick={this.handleSubmit}
onClick={this.handleName}
/>
)}
/>
}
So my question is how to put 2 onClick function to the Hompage.js
How about this:
<HomePage
item={this.state.SaveFavorite}
onClick={(favorite)=>{
this.handleSubmit(favorite);
this.handleName(favorite);
}
}
/>
This assumes your goal is to call both functions one at a time. If they should be called in different situations give one function a different name, eg onSubmit or onNameChange.
Try This:
<HomePage
item={this.state.SaveFavorite}
onClick={(newFavorite) => this.handleSubmit(newFavorite);this.handleName(newFavorite)}
/>
you can pass multiple functions to events in react, let say changeEvent, to do follow those steps.
1- create your function two or the number of function you like.
2- create an object that contains those functions
3- pass the object as a props to where it would be consumed
4- choose the correspondant function to each form or whatever you need.
here is an example, this sample is with typescript.
const onChangeFunctions = {
onChangeForm1: handleChangeForm1,
onChangeForm2: handleChangeForm2,
};
<MainForm
onChange={onChangeFunctions} // here is your function
datas={yourData}
otherProps={otherProps}
/>
Now you use the fucntion on the child components
interface PropsFrom {
model1: Model1;
model2: Model2;
onChange: any;
}
export default function ProductForm(props: PropsForm) {
return (
<Container maxWidth="lg">
<Grid item md={6}>
<FormOne
model={props.model1} // the model that bind your form
onChange={props.onChange.onChangeForm1} // here you can use your first function
/>
</Grid>
<Grid item md={6}>
<FormTwo
model={props.model2} // the model that bind your form
onChange={props.onChange.onChangeForm2} // here you can use your second function
/>
</Grid>
</Grid>
</Container>
for javascript just pass the functions as props and delete the interface from the child components.
I know there are many answers out there for this issue but I couldn't find one that exactly solved my problem. I am getting the following error : Warning: Each child in an array or iterator should have a unique "key" prop. Check the render method of QuestionItem. See https://fb.me/react-warning-keys for more information.
I am setting a key for the component but I can't get the warning to go away.
Main component :
renderData() {
return this.state.data.map((data) => {
return (
<QuestionItem key={data._id} data={data} delete={this.deleteItem} edit={this.editItem} />
)
})
}
QuestionItem component :
import React, { Component, PropTypes } from 'react';
import Card from 'material-ui/lib/card/card';
import CardActions from 'material-ui/lib/card/card-actions';
import CardHeader from 'material-ui/lib/card/card-header';
import CardMedia from 'material-ui/lib/card/card-media';
import CardTitle from 'material-ui/lib/card/card-title';
import FlatButton from 'material-ui/lib/flat-button';
import CardText from 'material-ui/lib/card/card-text';
import Delete from 'material-ui/lib/svg-icons/action/delete';
import ModeEdit from 'material-ui/lib/svg-icons/editor/mode-edit';
import IconButton from 'material-ui/lib/icon-button';
import Button from '../UI/Button';
class QuestionItem extends Component {
renderTags() {
return this.props.data.tag.map((tag) => {
return (
<FlatButton label={tag} />
)
})
}
renderCompany() {
return this.props.data.company.map((company) => {
return (
<FlatButton label={company} />
)
})
}
edit = () => {
this.props.edit(this.props.data);
}
delete = () => {
this.props.delete(this.props.data._id);
console.log(this.props.data._id);
}
render() {
return (
<Card style={{margin: 50}}>
<CardTitle title={this.props.data.text} />
<CardText>
{this.props.data.answer}
</CardText>
<CardActions>
{ this.renderTags() }
{ this.renderCompany() }
<IconButton onClick={this.delete} style={{float: 'right'}}>
<Delete />
</IconButton>
<IconButton onClick={this.edit} style={{float: 'right'}}>
<ModeEdit />
</IconButton>
</CardActions>
</Card>
)
}
}
export default QuestionItem;
What am I missing here?
Well you'll need to log out the data._id and verify that they are all unique. Or you can do this:
renderData() {
return this.state.data.map((data, index) => {
return (
<QuestionItem key={index} data={data} delete={this.deleteItem} edit-{this.editItem} />
);
});
}
As the other answer pointed out, the other calls to map that go to a render need to set the key prop too to a unique value.
So these:
renderTags() {
return this.props.data.tag.map((tag) => {
return (
<FlatButton label={tag} />
)
})
}
renderCompany() {
return this.props.data.company.map((company) => {
return (
<FlatButton label={company} />
)
})
}
Should become:
renderTags() {
return this.props.data.tag.map((tag, index) => {
return (
<FlatButton key={index} label={tag} />
);
});
}
renderCompany() {
return this.props.data.company.map((company, index) => {
return (
<FlatButton key={index} label={company} />
);
});
}
Note we are using index which is the array index. It is basically like a synthetic identifier in SQL. If what you're actually rendering has unique identifiers already, it is better to use those! For example, the key prop for a tag could be just the tag -- the string itself. The key prop supports multiple types:
react - nodes-and-elements:
key : string | boolean | number | null,
So if your tags are unique (I would expect them to be but obviously don't want to assume), you could do this:
renderTags() {
return this.props.data.tag.map((tag) => {
return (
<FlatButton key={tag} label={tag} />
);
});
}
You might consider doing instead something like (tag || '').toLowerCase().replace(' ', '_') however I think React is already doing some manipulation there (besides potentially character case). So just passing the tag itself should be good! You can inspect the DOM to see data-reactid if you're not running a version that got rid of it (I think 0.15 gets rid of it). The React developer tools might let you inspect the key with 0.15.
Update
I do not recommend using the array index as the key. It causes subtle bugs. To see this in action, make an array of objects, render them using the array index and then mutate the array by removing say the 2nd element (and ensure React renders again). Now the indexes don't correspond to the same objects. My recommendation is to always set a key to a unique value. During development, it might be best not to set a key until you find one rather than using the array index because then the errors on the console will remind you to fix this before deploying/committing your change.
In renderTags() and renderCompany() you have iterators with no keys.
I am trying to access the props in my child component, I am rendering a grid row using a mapped json result:
getRowNodes: function() {
return this.props.contacts.map(function(contact){
return <Row
key={contact.id}
contact={contact}
columns={this.props.children} />;
}.bind(this));
}
When I render the component I can console log {this.props.data} and see all the properties, I can also see all the properties in chrome dev tools, however, when I try and access a property this.props.data.propertyName I get undefined.
If I try and access any of the properties below I get an error..any ideas?
Like Kirill Slatin said: You have to wrap it.
Try this:
getRowNodes() {
return (
<div>
{this.props.contacts.map(this._getRow)}
</div>
);
},
_getRow(contact) {
return (
<Row
key={contact.id}
contact={contact}
columns={this.props.children} />
);
}
NOTE: I have optimized the readability by using JSX Syntax.