Why is my component not rendering a prop value? - javascript

I have a simple component tree:
RecipeFeed which renders Recipe
In my RecipeFeed component I map an an array of recipe objects to render a Recipe component for each recipe object in the array
const renderRecipes = () => {
if (recipes) {
return (
<div>
{recipes.map(recipe =>
<Recipe
key={recipe.id}
recipe={recipe}
ingredients={recipe.ingredients}/>
)}
</div>
)
} else {
return (
<div>
....loading
</div>
)
}
}
My Recipe component is similar:
const renderIngredients = () => {
if (props.ingredients) {
props.ingredients.map(ingredient => {
console.log(ingredient.name)
<div>{ingredient.name}</div>
})
}
}
return (
<div>
<div>{props.recipe.name}</div>
{renderIngredients()}
</div>
)
}
My recipe.name is OK, and renders to the DOM. However, although my ingredients are defined in the console.log in Recipe.jsx, Nothing is rendered to the screen.
I believe it must have something to do with the shape of the data and the way I am trying to access the value, but I am confused why it appears to be okay when inspecting the console log - right value type of string, no errors, etc.
The json data looks like this
"data": [
{
"id": 2,
"name": "pizza",
"ingredients": [
{
"id": 5,
"name": "dough",
"food_group": "grain",
"created_at": "2022-03-08T04:39:41.334Z",
"updated_at": "2022-03-08T04:39:41.334Z"
},
{
"id": 6,
"name": "sauce",
"food_group": "vegetable",
"created_at": "2022-03-08T04:40:11.684Z",
"updated_at": "2022-03-08T04:40:11.684Z"
},
{
"id": 7,
"name": "cheese",
"food_group": "dairy",
"created_at": "2022-03-08T04:40:33.032Z",
"updated_at": "2022-03-08T04:40:33.032Z"
}
],
"recipe_ingredients": [
{
"id": 3,
"recipe_id": 2,
"ingredient_id": 5,
"quantity": null,
"measurement_unit": null,
"created_at": "2022-03-08T04:41:06.482Z",
"updated_at": "2022-03-08T04:41:06.482Z"
},
{
"id": 4,
"recipe_id": 2,
"ingredient_id": 6,
"quantity": null,
"measurement_unit": null,
"created_at": "2022-03-08T04:41:06.484Z",
"updated_at": "2022-03-08T04:41:06.484Z"
},
{
"id": 5,
"recipe_id": 2,
"ingredient_id": 7,
"quantity": null,
"measurement_unit": null,
"created_at": "2022-03-08T04:41:06.485Z",
"updated_at": "2022-03-08T04:41:06.485Z"
}
]
}

You've got numerous mistakes here.
First: the second return statement is not inside a function. It's not part of the renderIngredients function -- which it can't be anyway, since then it would call itself. (Yes, recursion is legit in javascript, but this case would be perfectly circular and thus break.)
(On second glance, this is maybe because you forgot to include the beginning of the Recipe component, but we're not mind readers here.)
Second: your renderIngredients function won't accomplish anything, because: (1) it does not return anything, and (2) the map inside it also doesn't return anything. This is the direct answer to your question, "Why is my component not rendering a prop value?": it's because you haven't chosen to return anything, either with the return keyword, or by using the short arrow form: () => returnValue.
Third: the signature of your Recipe component is bad:
<Recipe
key={recipe.id}
recipe={recipe}
ingredients={recipe.ingredients}
/>
Why are you passing both the recipe and the ingredients separately? The component can access the ingredients through the recipe. You've set it up so that the same data has to be fed into it twice.
Fourth: the names of component functions must start with a capital letter so that the React framework recognizes them as custom components instead of native HTML components. And don't name your component "renderRecipes", name it "RecipeList".

Related

JS Filter array inside array by fields [duplicate]

This question already has answers here:
Filtering array of objects with arrays based on nested value
(8 answers)
Closed last month.
Json structure:
[
{
"profile_id": 1,
"nicknames": [
{
"nickname_id": 230,
"nickname_value": "Rob",
"admin_id": 5,
"created_at": "2023-01-08T13:20:24.000000Z",
"updated_at": "2023-01-08T13:20:24.000000Z"
},
{
"nickname_id": 231,
"nickname_value": "Wel",
"admin_id": 5,
"created_at": "2023-01-08T13:20:32.000000Z",
"updated_at": "2023-01-08T13:20:32.000000Z"
}
]
},
{
"profile_id": 12,
"nicknames": [
{
"nickname_id": 232,
"nickname_value": "Pol",
"admin_id": 5,
"created_at": "2023-01-08T14:06:14.000000Z",
"updated_at": "2023-01-08T14:06:14.000000Z"
}
]
}
]
I would like to have for example HTML input where I may provide some value. I need that script filter array by fields nickname_value, nickname_id, and profile_id from only one input field. I know how to filter inside an array but I don't have an idea to add a filter by profile_id.
const data = [{
"profile_id": 1,
"nicknames": [{
"nickname_id": 230,
"nickname_value": "Rob",
"admin_id": 5,
"created_at": "2023-01-08T13:20:24.000000Z",
"updated_at": "2023-01-08T13:20:24.000000Z"
},
{
"nickname_id": 231,
"nickname_value": "Wel",
"admin_id": 5,
"created_at": "2023-01-08T13:20:32.000000Z",
"updated_at": "2023-01-08T13:20:32.000000Z"
}
]
},
{
"profile_id": 12,
"nicknames": [{
"nickname_id": 232,
"nickname_value": "Pol",
"admin_id": 5,
"created_at": "2023-01-08T14:06:14.000000Z",
"updated_at": "2023-01-08T14:06:14.000000Z"
}]
}
]
document.querySelector('input').addEventListener('input', ({
target: {
value
}
}) => {
const matches = data.filter(
profile => profile.profile_id.toString() === value|| profile.nicknames.some(
nickname => nickname.nickname_id.toString() === value || nickname.nickname_value === value
)
)
document.querySelector('#results').innerHTML = matches.map(profile => profile.profile_id).join('')
})
<input>
<div id="results"></div>
The problem gets simple if we can clearly state what we mean by a match, and the code gets simpler to write (and read) if we describe the predicates as functions.
Here's a proposal that says an object matches an input if the profile_id matches exactly or if any of its nicknames match. A nickname matches if the nickname_id matches exactly or if the input is a case-insensitive prefix of the nickname_value.
These criteria are a reasonable-seeming guess based on the prop names, but really up to the app.
// answer whether the object "matches" the input. presumes input is a string
function match(object, input) {
if (object.profile_id === +input) return true;
const nicknameMatch = n => n.nickname_id === +input ||
n.nickname_value.toLowerCase().startsWith(input.toLowerCase());
return object.nicknames.some(nicknameMatch)
}
With this, filter works straightforwardly...
const results = input.filter(object => match(object, input))
Once this is working, we might want to think about efficiency. One idea would be to cache a few things with the object, like stringified ids, and arrays of nickname ids and lowercase nickname values. If these objects are important to an app that's apt to grow, we could convert the literal objects to instances of a class, and give them a match method.

Where am i going wrong with this array filter? Reactjs

I am mapping over some data that I am getting from a api however when i try to add the filter function i get 'currencyData.includes is not a function'
I have also tried just hard coding the array but it also still doesnt work?
I have a loading state for when i fetch data from the api which holds code from being run but i have removed it from this example as its not getting data from the api below.
The simplified version is here...
ARRAY
var items = [
{
"id": 1,
"productName": "shoes",
"productIdentifier": "CL001",
"productDescription": "adidas kicks boir",
"productPrice": 2000,
"productStock": 200,
"created_at": "2020-51-28",
"updated_at": null
},
{
"id": 2,
"productName": "burger",
"productIdentifier": "FD001",
"productDescription": "charsiu berger",
"productPrice": 2000,
"productStock": 200,
"created_at": "2020-51-28",
"updated_at": null
}
]
return(
{items.filter(currencyInfo => currencyInfo.includes("FD001")).map((value, index) => {
console.log(value)
return(
<h1 key={index}>{value}</h1>
)
})}
)
currencyInfo is not an array, you can not call includes on it
Here is my suggestion:
return(
{items.filter(currencyInfo => currencyInfo.productIdentifier === "FD001").map((value, index) => {
console.log(value)
return(
<h1 key={index}>{value}</h1>
)
})}
)
More about includes()
The includes() method determines whether an array includes a certain value among its entries, returning true or false as appropriate. Check this Doc
But in items.filter(currencyInfo => currencyInfo.includes("FD001")), type of currencyInfo isn't array but object.
So you should use currencyInfo.productIdentifier.includes()

JSX filter items in array if property matches string, otherwise show all items

I have created some fake "tasks" json and I need to list it out in JSX which I have done. Now I have some buttons that allow you to view tasks by "All", "Quotes" or "Other". Each task in the JSON file has a "type" property which either has "Quotes" or "Other".
Here is the code where I map through the array and show the information for each task:
<section className={styles.tasks_list}>
{tasksData.map(({ customer_id, account_name, days }) => {
return (
<article key={customer_id} className={styles.task}>
<span className={styles.task_name}>{account_name}</span>
<span className={`${styles.task_days}`}>{getDaysDue(days)}</span>
<div className={styles.task_buttons}>
<button className={`${styles.task_button} ${styles.edit}`}>
<MdModeEdit />
</button>
<button className={`${styles.task_button} ${styles.delete}`}>
<MdDeleteForever />
</button>
</div>
</article>
);
})}
</section>
Of course this displays ALL data.
I have state which updates and saves the current active tab's value - so if the quote button is selected, I can access the variable activeTab and get the string "Quotes", same for "Other". When the All button is clicked it changes this string to "All". Now none of my json objects have the "All" type because I want it to display everything regardless of type.
Here is the json:
[
{
"account_name": "Misty's Gym",
"customer_id": 1,
"days": {
"days_due": 1,
"overdue": false
},
"type": "Quotes"
},
{
"account_name": "Brock's Diner",
"customer_id": 2,
"days": {
"days_due": 0,
"overdue": false
},
"type": "Quotes"
},
{
"account_name": "Samurai Champloo's Fish Bar",
"customer_id": 3,
"days": {
"days_due": 5,
"overdue": false
},
"type": "SSL Setup"
},
{
"account_name": "Tiny Rebel",
"customer_id": 4,
"days": {
"days_due": 7,
"overdue": true
},
"type": "Domains"
},
{
"account_name": "Matalan",
"customer_id": 5,
"days": {
"days_due": 13,
"overdue": false
},
"type": "Other"
},
{
"account_name": "Lowes Soft Drinks",
"customer_id": 6,
"days": {
"days_due": 1,
"overdue": false
},
"type": "SEO Setup"
},
{
"account_name": "Snack 'n' Go",
"customer_id": 7,
"days": {
"days_due": 2,
"overdue": true
},
"type": "Quotes"
},
{
"account_name": "Jeronemo",
"customer_id": 8,
"days": {
"days_due": 5,
"overdue": false
},
"type": "Quotes"
}
]
How can I filter by the strings but then show all of them if the activeTab variable is set to "All"? I might be overthinking this. I've run an if statement where it shows the items if (task.type === activeTab) { shit out jsx with information where the type matches } but I don't know how to then show all.. if I was to do an else statement I'd need to copy the exact same JSX which I want to avoid. Also it causes all tabs to just show every task.
I feel like there's something obvious I'm not thinking about. My code is a mess and it's almost 5am, should probably sleep!
Any help appreciated, thank you.
We just need to change the data before map. I am just showing the changed part in your code.
// ... same code
tasksData.filter((task) => (activeTab === 'All' ? true : task.type === activeTab)).map
// ... same code
In here we are looping over the task list beforehand and only accepting the task whose task.type is equal to activeTab, with just a tweak that we will use this check only if activeTab is not equal to All
The algorithm is pretty much as you have defined in your question. From your question, it isn't clear why 'Also it causes all tabs to just show every task'
I would use the following logic to handle this.
If activeTab === options
Filter tasks with type options and display
else if activeTab === quotes
Filter tasks with type quotes and display
else if activeTab === all
Display all tasks
I don't see why this has any problems in logic, so it shouldn't display every task in all tabs.

React cannot map through prop array

I fetch an api on componentDIdMount() then store the json to a state then I pass that state as a prop, I have no problem showing the data except on arrays.
<Component details={this.state.details} />
json:
{
"adult": false,
"backdrop_path": "/qonBhlm0UjuKX2sH7e73pnG0454.jpg",
"belongs_to_collection": null,
"budget": 90000000,
"genres": [
{
"id": 28,
"name": "Action"
},
{
"id": 878,
"name": "Science Fiction"
},
{
"id": 35,
"name": "Comedy"
},
{
"id": 10751,
"name": "Family"
}
]
}
then I try to map the genres:
<div className={style.genre}>
{details.genres.map(g => (
<span key={g.id}>{g.name}</span>
))}
</div>
But then I get Cannot read property 'map' of undefined, I don't know why this is happening because I'm able to do details.budget
It's trying to read data before you get the result from api.
so write the map function as
{details&&details.genres&&details.genres.map(g => (
<span key={g.id}>{g.name}</span>
))}
In react Initially when component is mounted, render() function is called and then componenentDidMount() is called in which you fetch data. So Initially details is empty. So you need to write the condition.

Generating components through iteration

Something weird is going on:
This is my initial state (a .js file)
import moment from 'moment';
let date = moment();
var previousDate = moment("2015-12-25");
export const projects = [
{
"id": 0,
"metadata": {
"fields":
[{
"id": 1,
"order": 1,
"name": "Name",
"value": "Collection 1"
},
{
"id": 2,
"order": 2,
"name": "Created On",
"value": date
},
{
"id": 3,
"order": 3,
"name": "Last Modified On",
"value": previousDate
},
{
"id": 4,
"order": 4,
"name": "Status",
"value": "Filed"
}],
"tags":
[{
"id": 1,
"order": 1,
"value": "tag1"
},
{
"id": 2,
"order": 2,
"value": "tag2"
},
{
"id": 3,
"order": 3,
"value": "tag3"
},
{
"id": 4,
"order": 4,
"value": "tag4"
}]
}
}
This is ProjectsList.js:
import React from 'react';
import Project from './Project';
import { projects } from 'initialState';
export default (props) => {
return(
<div className="projectsList">
{projects.map(project => (
<article key={project.id}><Project fields={project.metadata.fields} /></article>
))}
</div>
)
}
And this one's Project.js:
import React from 'react';
export default (props) => {
return(
<ul className="fields">
{props.fields.map(field => <li key={field.id}>{field.name}</li>) }
</ul>
)
}
I am trying to render a bunch of projects in a list, and every project contains a bunch of metadata key-value pairs that it shows.
So basically, the wiring does not matter, it all works fine.
Except for this:
If you look up at the initial state file (first one up there), there is an array of multiple objects in fields. Each object shows 4 key-value pairs
id
order
name
value
Now, in Project.js, the line where I go
{props.fields.map(field => <li key={field.id}>{field.name}</li>) }
looks like I can switch the {field.name} for {field.id}, to show the id in text. Or I can go {field.order}, to display the order.
But weirdly enough, if I want to show the actual value of the field, like so {field.value}, it throws.
invariant.js?4599:38
Uncaught Invariant Violation: Objects are not valid as a React child (found: Mon Jun 20 2016 21:40:33 GMT-0400). 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 `StatelessComponent`.
I even went as far (sigh) as changing the string value in every fields to val, juste to make sure value wasn't some kind of a reserved word.
Still threw.
Anybody can help me understand what I have done wrong, here?
Thanks Guys.
You are assigning to variable values to the value property in your state file, which are most likely not strings, but objects:
export const projects = [{
"id": 0,
"metadata": {
"fields":
[
...
{
"id": 2,
"order": 2,
"name": "Created On",
"value": date // one
},
{
"id": 3,
"order": 3,
"name": "Last Modified On",
"value": previousDate // and another one
},
...
]
...
}
}
If typeof children returns "object" (and children is neither an array, nor a ReactElement), it throws:
https://github.com/facebook/react/blob/dc6fc8cc0726458a14f0544a30514af208d0098b/src/shared/utils/traverseAllChildren.js#L169
Here's a simplest example to demonstrate this:
const IllegalComponent = () => <span>{{}}</span>
You are supposed to supply a string (or number) so that React could inline that as the children in <li>. Children should be something that's renderable and implements ReactNode.
If the children is an object, React would not know how to render it. You should explicitly convert the value to String.
Try this to see if it works:
{props.fields.map(field => <li key={field.id}>{field.value.toString()}</li>) }

Categories

Resources