I'm having a strange issue that I can't pin down with React (I'm using CoffeeScript as well, but I highly doubt this is a factor). Basically, I'm following along with a tutorial in which a message feed is built using a Feed component (the parent), FeedList component (child), and a FeedItem (grandchild)...sorry if my terminology is incorrect. The relevant code is:
Feed.cjsx
getInitialState: ->
FEED_ITEMS = [
{ key: 1, title: 'Realtime data!', description: 'Firebase is cool', voteCount: 49 }
{ key: 2, title: 'JavaScript is fun', description: 'Lexical scoping FTW', voteCount: 34 }
{ key: 3, title: 'Coffee makes you awake', description: 'Drink responsibly', voteCount: 15 }
]
{
items: FEED_ITEMS
formDisplayed: false
}
...
render: ->
...
<FeedList items={#state.items} onVote={#onVote} />
FeedList.cjsx
render: ->
feedItems = #props.items.map ((item) ->
<FeedItem key={item.key} ... />
).bind(#)
<ul className='list-group container'>
{feedItems}
</ul>
FeedItem.cjsx
render: ->
<li key={#props.key} className='list-group-item'>
...
</li>
If I console.log "#props.key" in the render method for FeedItem, I get undefined. But if I log "item.key" from inside the map function of FeedList's render method, I get 1, 2, 3, as I should. So it seems to me that, for whatever reason, React doesn't want to pass the "key" prop to the FeedItem. Any thoughts?
For anyone else stumbling across this, react only has a few reserved props but they are worth noting. key, ref, __self and __source.
var RESERVED_PROPS = {
key: true,
ref: true,
__self: true,
__source: true
};
^^ Taken from the react source.
Also worth noting __self={this} is really useful if you're receiving invariant violation errors and would like to be able to debug them down to a component level.
Since react treats key as a special attribute (http://facebook.github.io/react/docs/special-non-dom-attributes.html), it cannot be accessed via the props. The react documentation also warns against setting keys within plain html tags (http://facebook.github.io/react/docs/multiple-components.html#dynamic-children), and suggests wrapping multiple components in a react component.
If you rename key to something non-reserved, it should work:
Feed.cjsx:
FEED_ITEMS = [
{ itemId: 1, title: 'Realtime data!', description: 'Firebase is cool', voteCount: 49 }
{ itemId: 2, title: 'JavaScript is fun', description: 'Lexical scoping FTW', voteCount: 34 }
{ itemId: 3, title: 'Coffee makes you awake', description: 'Drink responsibly', voteCount: 15 }
]
then you can access the itemId via #props.itemId in the child component (FeedList).
FeedList:
feedItems = #props.items.map ((item) ->
<FeedItem key={item.itemId} ... />
).bind(#)
Note that the keys for each component need to be unique for each component, or node in the DOM, which is why it makes sense that keys cannot be inherited, as setting both parent and child to the same key would not allow react to identify them as separate entities when rendering the DOM.
Related
I have a data-table using Vuetify that passes a localAuthority prop from a rails backend. It all works really well until I pass an empty child association (nested attribute). In this case 'county':
<script>
import axios from "axios";
export default {
name: 'LocalAuthorityIndex',
props: {
localAuthorities: {type: Array, default: () => []},
counties: {type: Array, default: () => []},
localAuthorityTypes: {type: Array, default: () => []}
},
data() {
return{
search: '',
dialog: false,
testmh: 'hello',
dialogDelete: false,
headers: [
{ text: 'Name', align: 'start', value: 'name' },
{ text: 'ONS Code', value: 'ons_code' },
{ text: 'ID', value: 'id' },
{ text: 'Population', value: 'population' },
{ text: 'county', value: 'county.name' },
{ text: 'Website', value: 'website' },
{ text: 'Actions', value: 'actions', sortable: false },
],
So in the example above it works as long as all records have a county association (belongs_to). However, if one record does not have a 'county' associated with it then I get the following error:
[Vue warn]: Error in render: "TypeError: Cannot read properties of undefined (reading 'name')"
I have tried lots of things like adding in a conditional statement like below:
{ text: 'county', value: ('county.name' ? 'county.name' : nil )},
But nothing seems to work.
According to your <v-data-table> code at Codepen, I see that you are overriding default table item slots with your own.
Your error are from this part of code:
...
<template #item.county.name="{ item }">
<a :href="'/counties/' + item.county_id">
{{ item.county.name }}
</a>
</template>
...
Take a look at first string. #item.county.name is a short form of v-slot:item.county.name and comes from one of your strings in headers array:
...
{ text: 'county', value: 'county.name' },
So there's no error, this part are correctly parsed by vuetify library even when your item will not contain any county.
The error is in 3rd string of the above code. You are trying to print name of county without checking its existence. That's why you are getting ...Cannot read properties of undefined... error.
I guess you may fix your issue this way:
<template #item.county.name="{ item }">
<a :href="'/counties/' + item.county_id">
{{ item.county ? item.county.name : 'No name' }}
</a>
</template>
Of course, if you need to hide your link to counties in this case, you may also add v-if (or v-show) into a tag.
I also created a small Codepen with some static data. Take a look into item.name.text slot in this playground, maybe it will help you to understand similar object associations.
I'm trying to output data from an object, but I get an error "TypeError: Cannot read property 'dropdownList' of undefined", before the Lesson array all objects arrive successfully, but then undefined appears, what could be the problem? everything else jsx removed for readability
https://ibb.co/hRytjMB
https://ibb.co/6F1YcBr
Lesson.jsx
{
this.props.lesson.dropdown.dropdownList.map(option =>
items.push(<option>{option.par}</option>)
)
}
lesson-reducer.js
import React from 'react';
import {faDotCircle} from "#fortawesome/free-solid-svg-icons/faDotCircle";
const ADD_LESSON = "ADD_LESSON";
const one = "Robby";
let initialState = {
lessonsTitle: [
{
iconName: faDotCircle,
description: [
<span className="yourClass">{one}</span>,
` advanced diverted domestic sex repeated bringing you old.`
],
dropdown: {
titleName: "JSON",
dropdownList: {
par: 'Hello World',
}
}
},
{
titleName: "Boolean",
iconName: faDotCircle,
description: `
Folly words widow one downs few age every seven. tolerably.
`,
languages: [
'Язык HTML2',
]
},
{
titleName: "Math",
iconName: faDotCircle,
description: `
Another journey chamber way yet females man.
`,
languages: [
'Язык HTML3',
]
},
],
};
It seems you have an incoherent structure in your reducer, causing the undefined error. For the the first object it should work because you do have a dropdown object, but the other two don't which may cause the issue.
Additionaly your dropdownList is an object when it seems it should be an array if you want to iterate over it.
you could try this verification to keep it clean:
this.props.dropdown?.dropdownList?.map(...)
The interrogation point there checks if the preceding value is null or undefined and if its not it provides access to its properties. Otherwise it skips whatever is in front and to the next code statement.
Also you should iterate lessons as it is an array
the reason you get TypeError: Cannot read property 'dropdownList' of undefined
is because you are trying to access this.props.lesson without an index, and its an array/
since this.props.lesson is an array, i think you want to use map() over this.props.lesson to achieve your goal
Suppose I have an array feedsArray, the example value may look like this:
this.feedsArray = [
{
id: 1,
type: 'Comment',
value: 'How are you today ?'
},
{
id: 2,
type: 'Meet',
name: 'Daily sync up'
}
]
Suppose I have registered two components: Comment and Meet, Each component has a prop setting as the following:
props: {
feed: Object
}
and the main component has the following definition:
<component v-for="feed in feedsArray" :feed="feed" :key="feed.id" :is="feed.type"></component>
As you can see, it uses is property to select different component. My question is, how to detect feed object change in the child component ? Like when I set
this.feedsArray[0] = {
id: 1,
type: 'Comment',
value: 'I am not ok'
}
How can the Comment component detect the changes ? I tried to add a watcher definition in the child component like the following:
watch: {
feed: {
handler (val) {
console.log('this feed is changed')
},
deep: true
}
},
But it doesn't work here. Anyone know how to solve this ?
Do not assign directly to an array using index - use splice() instead, otherwise JavaScript can not detect that you have changed the array.
If you want to change only the value of an already existing key of an object - then simply update it e.g. this.feeds[0].value = 'I am not okay any more';
This works for existing keys only - otherwise you have to use this.$set(this.feeds[0], 'value', 'I am not okay any more');
I have a simple React project that in short is like a little restaurant. And the restaurant can only serve certain dishes when all ingredients are available.
So I wrote something to check for each dish that all ingredients are available / true. But within my JSX I am getting the following error TypeError: Cannot read property 'status' of undefined (screenshot provided).
However where you can see I have a console.log at // 1., the status property is defined.
Link to my public repo: https://github.com/danny-faith/dynamenu/tree/update/ingre-object
Use update/ingre-object branch
Ingredients object
const ingredients = {
egg: {
status: true
},
beef: {
status: false
}
}
Menu object
const food = {
item1: {
name: "Spaghetti Carbonara",
desc: "With egg yolk and guanciale",
price: 1724,
ingredients: ['spaghetti','egg','pancetta'],
status: "available"
}, ...
}
Code insinde JSX
{
Object.keys(this.state.menu).map(key => {
console.log(this.state.ingredients['egg'].status); // 1.
if (menu[key].ingredients.some(val => this.state.ingredients[val].status === true) ){
return <MenuItem key={key} data={menu[key]} />
}
})
}
I dont see where you set your state , but this error can simply be avoided by adding checks state.ingredients like below.
if (menu[key].ingredients
.every(val => {
return this.state.ingredients[val] ?
this.state.ingredients[val].status === true :
false;
})){
return <MenuItem key={key} data={menu[key]} />
}
A better practice would be to take the if condition to a separate function.
EDIT, added extra closing parenthesis to complete .some() function.
Since you are mapping an object maybe the problem is not on the specific ingredient that you log.
I think Cannot read property 'status' of undefined referred to this.state.ingredients[val].status depends on a val that is not defined on your ingredients object nested in one of the menu properties.
You probably have a menu ingredient that is not present in the ingredients.
[EDIT]
Looking at your repo I see:
item1: {
name: "Spaghetti Carbonara",
desc:
"With egg yolk and guanciale",
price: 1724,
ingredients: ['egg','spaghetti','pancetta'],
status: "available"
},
...
And your ingredients are:
const ingredients = {
egg: {
status: true
},
beef: {
status: false
}
}
So, for instance, ingredients['spaghetti'] or ingredients['pancetta'] are undefined.
I am trying to create a React/Redux app which lists books. Each book then has related books. I know I should structure my redux store in some sort of normalized fashion like so:
{
books: {
'book1id': {
title: 'Book 1',
author: 'Author 1',
relatedBooks: ['book2id', 'book3id']
},
'book2id': {
title: 'Book 2',
author: 'Author 2',
relatedBooks: ['book1id']
}
}
}
and load each book as necessary.
The issue is where to store loading/error data from the API requests? Some ideas I had were to create an object for every book such as
books: {
'book1id': {
isLoading: false,
error: null,
book: {
title: 'Book 1',
...
}
},
...
}
But that seems to detract slightly from the readability/intuitiveness of the state. Is there a better way to structure the state?
I structure my redux store so that it includes an entities object for all of my relational data and I store things specific to a page or a set of routes in separate parts of the state. My state tree might look something like this:
const state = {
entities: {
books: {
...
},
authors: {
...
},
...
},
booksPage: {
isLoading: false
}
}
Now I am keeping track of my relational state separate from the state of my UI components. I would not recommend trying to store a isLoading state in an individual entity since that entity may or may not exist. If you need more granular loading/error state on a per entity basis then rather on a set of entities you have a few options. The first option is keep a set of IDs that are currently loading. This is not a very practical solution because you can't track success or failure with an ID simply being in a set or not.
The next, better solution is to keep a map from ID to a status object that includes if an individual entity is loading, if it was successful or if it failed to load and what the errors were.
const state = {
entities: {
books: {
...
},
authors: {
...
},
...
},
booksPage: {
loading: {
book1: {
status: 'FAILED',
error: 'Network request failed.'
},
book2: {
status: 'SUCCESS',
},
book3: {,
status: 'IN_PROGRESS'
}
}
}
}
In summary, I find separating out your relational state into an entities child object while having page specific sections of state to be working quite well for me.