I have something like this, where I would like to create array in the state from a variable initialized directly above it. I get the error cards is not defined. Is there a way around this? I need to set this array specifically in the state.
class Example extends React.Component {
constructor(props) {
super(props);
this.state = {
cards: [
{
name: "Name 1",
description: "dfdsfaf",
},
{
name: "Name 2",
description: "dsfsfasf",
},
{
name: "Name 3",
description: "daslkdjadlajsd",
},
],
names: cards.map(item => item.name)
};
}
...
}
You can do this in javascript as follows:
const cards = [...]
const names = = cards.map(...)
this.state = { cards: cards, names: names }
You should probably not do this though and set state to only the cards and move the call to calculate the names to your render method
Related
I want to take data from js files classified as categories such as 'Delivery' and 'Cafe' and deliver different data to different pages.
I thought about how to import it using map(), but I keep getting errors such as 'products' is not defined.'
It must be done, but it is not implemented well with javascript and react weak. If you know how to do it, I'd appreciate it if you could let me know.
Products.js
export const Product = [
{
Delivery: [
{
id: '101',
productName: '허니랩',
summary: '밀랍으로 만든 친환경 식품포장랩 허니랩.',
description:
'~~',
images: ['3k7sH9F'],
companyName: '허니랩',
contact: '02-6082-2720',
email: 'lesslabs#naver.com',
url: 'https://honeywrap.co.kr/',
},
{
id: '102',
productName: '허니포켓',
summary: '밀랍으로 만든 친환경 식품포장랩 허니랩. 주머니형태.',
description:
"~~",
images: ['4zJEqwN'],
companyName: '허니랩',
contact: "02-6082-2720",
email: "lesslabs#naver.com",
url: "https://honeywrap.co.kr/",
},
],
},
{
HouseholdGoods: [
{
id: '201',
productName: '순둥이',
summary: '아기용 친환경 순한 물티슈',
description:
'~',
images: ['4QXJJaz'],
companyName: '수오미',
contact: '080-000-3706',
email: 'help#sumomi.co.kr',
url: 'https://www.suomi.co.kr/main/index.php',
},
{
id: '202',
category: ['HouseholdGoods'],
productName: '순둥이 데일리',
summary: '친환경 순한 물티슈',
description: '품질은 그대로이나 가격을 낮춘 경제적인 생활 물티슈',
images: ['OMplkd2'],
companyName: '수오미',
contact: '080-000-3706',
email: 'help#sumomi.co.kr',
url: 'https://www.suomi.co.kr/main/index.php',
},
],
},
];
Delivery.js
(The file was named temporarily because I did not know how to classify and deliver data without creating a js file separately.)
import React from "react";
function Delivery(
productName,
companyName,
contact,
email,
url,
summary,
description
) {
return (
<div className="Product">
<div className="Product__data">
<h3 className="Product__name">{productName}</h3>
<h4>{companyName}</h4>
<h5>Contact: {contact}</h5>
<h5>Email: {email}</h5>
<h5>URL: {url}</h5>
<p className="Product__summary">{summary}</p>
<p className="Proudct__descriptions">{description}</p>
</div>
</div>
);
}
export default Delivery;
Category.js
import React from "react";
import Delivery from "./Delivery";
import { Product } from "./Products";
class Category extends React.Component {
render() {
state = {
products: [],
};
this.setState(_renderProduct());
return <div>{products ? this._renderProduct() : "nothing"}</div>;
}
_renderProduct = () => {
const { products } = this.state;
const renderProducts = products.map((product, id) => {
return (
<Delivery
productName={Product.productName}
companyName={Product.companyName}
contact={Product.contact}
email={Product.email}
url={Product.url}
summary={Product.summary}
description={Product.description}
/>
);
});
};
}
export default Category;
Sorry and thank you for the long question.
There are quite a few different problems I've found.
First is that you call setState inside render in the Category component, this causes an infinite loop. Instead call setState inside a lifecycle method like componentDidMount or use the useEffect hook if using functional components.
Another problem is that state in Category is also defined inside render. In class components you would normally put this in a class constructor outside of render.
In your setState call you refer to _renderProduct(), this should be this._renderProduct() instead.
Now the main problem here is the structure of your data / how you render this structure.
Products is an array of objects where each object either has a Delivery or HouseholdGoods property which is an array of products. I would advise you to change this structure to something more like this:
export const Product = {
Delivery: [
{
id: "101",
},
{
id: "102",
},
],
HouseholdGoods: [
{
id: "201",
},
{
id: "202",
},
],
};
or this:
export const Product = [
{ id: "101", productType: "Delivery" },
{ id: "102", productType: "Delivery" },
{ id: "201", productType: "HouseholdGoods" },
{ id: "202", productType: "HouseholdGoods" },
];
I personally prefer the second structure, but I've implemented the first as this seems to be what you were going for:
class Category extends React.Component {
constructor(props) {
super(props);
this.state = {
products: null,
};
}
componentDidMount() {
this.setState({ products: Product });
}
render() {
const { products } = this.state;
return (
<div>
{products
? Object.keys(products).map((productKey) => {
return (
<div key={productKey}>
{products[productKey].map((product) => {
return (
<Delivery
key={product.id}
productName={product.productName}
companyName={product.companyName}
contact={product.contact}
email={product.email}
url={product.url}
summary={product.summary}
description={product.description}
/>
);
})}
</div>
);
})
: "no products"}
</div>
);
}
}
We need a nested loop here, because we need to map over each property key and over the array of objects inside each property. If you use the other structure for Product I've shown, you can simply map over Product without needing two loops.
Now the last important problem was that you weren't destructuring the props inside your Delivery component, instead you should do something like this:
function Delivery({
productName,
companyName,
contact,
email,
url,
summary,
description,
}) {
return (
<div className="Product">
<div className="Product__data">
<h3 className="Product__name">{productName}</h3>
<h4>{companyName}</h4>
<h5>Contact: {contact}</h5>
<h5>Email: {email}</h5>
<h5>URL: {url}</h5>
<p className="Product__summary">{summary}</p>
<p className="Proudct__descriptions">{description}</p>
</div>
</div>
);
}
Example Sandbox
I am passing an array data from parent to child component and I have encountered the following situations:
parent.component.html:
<child-component
...
[options]="students"
>
</child-component>
Status I: When I set the array on definition, everything is ok and I can get the array values on the child component.
parent.component.ts:
export class ParentComponent implements OnInit {
students: any[] = [
{ name: "Mary" },
{ name: "Marta" },
{ name: "Kelly" },
{ name: "John" },
{ name: "Shelley" },
{ name: "Frankstein" },
{ name: "Shierley" },
{ name: "Igor" }
];
}
child.component.ts:
export class ChildComponent implements OnInit {
#Input() options: any[]= [];
}
Status II: However, when I set the array on a method instead of definition, I get the input value as null. Why I want to fill the array in a method is that I fill it by retrieving data from server. So, I struggled with this problem and finally found that the problem is not related to async data. It is related to this definition place. So, how can I perform the data can be passed by its array values?
parent.component.ts:
export class ParentComponent implements OnInit {
students: any[] = [];
ngOnInit() {
this.getStudents();
}
getStudents() {
this.students = [
{ name: "Mary" },
{ name: "Marta" },
{ name: "Kelly" },
{ name: "John" },
{ name: "Shelley" },
{ name: "Frankstein" },
{ name: "Shierley" },
{ name: "Igor" }
];
}
Note: I think assigning null to the students on defining it. But otherwise it throws error and I encounter null value exception on child. Maybe lifcel-ycle related problem, but I have already tried ngOnchanges, ngAfterViewInit, etc.
If you do not directly assign an object which is passed to a child component, initially the input will receive undefined or an empty array since you are assigning an empty array to students variable.
To avoid it use conditional rendering with ngIf:
<child-component
*ngIf="students && students.length > 0"
[options]="students"
>
</child-component>
I'm trying to get a simple list of lessons contained in a course from an endpoint.
If I try console.log(this.state.course.lessons) an array contained 3 items is displayed.
However,if I try console.log(this.state.course.lessons.map(//function) I keep getting
TypeError: this.state.course.lessons is undefined
How can I map a function to the lessons array so I can render them as a list.
component
import React, { Component } from 'react'
export class CourseDetail extends Component {
constructor(props) {
super(props);
this.state = {
course: []
};
}
componentDidMount() {
fetch(`http://127.0.0.1:8000/api/courses/${this.props.match.params.id}`)
.then(res => res.json())
.then((course) => {
this.setState({
course: course,
});
console.log(this.state.course.lessons)
});
}
render() {
return (
<div>
{this.state.course.lessons.map((lesson)=>(console.log(lesson)))}
<h1>{this.state.course.title}</h1>
</div>
)
}
}
export default CourseDetail
json returned from end point
{
"id": 1,
"lessons": [
1,
2,
3
],
"owner": 1,
"rating": 0,
"title": "Course 1",
"description": "course 1 desc",
"pub_date": "2019-11-23",
"is_live": false,
"category": 1
}
Most obvious solution would be just to give the object a default state if you want to access it:
constructor(props) {
super(props);
this.state = {
course: {
lessons: []
}
};
}
The problem on your code is the life cycles (Mount-> Render-> DidMount), and in this render, u have not fetch the data yet.
You can try this:
render() {
if (!this.state.course.lessons) return null //this line
return (
<div>
{this.state.course.lessons.map((lesson)=>(console.log(lesson)))}
<h1>{this.state.course.title}</h1>
</div>
)
}
Is it possible to access a state value in constructor, and use it in a different state value in same constructor? See example below.
class App extends Component {
constructor(props){
super(props);
this.state = {
values1: {
value1: "value 1"
},
values2: {
value2: "value 2"
},
selected: {
selectedValue: `Selected value is: ${this.state.values1.value1}`
}
}
}
}
It will not work.
But you can set another variable's value in componentDidMount.
See my example :
class App extends Component {
constructor(props){
super(props);
this.state = {
values1: {
value1: "value 1"
},
values2: {
value2: "value 2"
},
}
}
componentDidMount(){
this.setState({selectedValue: "Selected value is: "+ this.state.values1.value1});
}
render() {
return (
<div>
<p>
{this.state.selectedValue}
</p>
</div>
)
}
}
Here is a working example - https://stackblitz.com/edit/react-2ra5ht
Yes it is but you would have to use the setState() method to do so
You would declare the selected state above then within the constructor you could then usethis.setState({selected: this.state.values1.value1}) to set the state.
Yes, you approach will work with small modification. state is plain JavaScript object, so you may assign new properties to it several times in constructor.
constructor () {
super ();
this.state = {
values1: {
value1: "value 1"
},
values2: {
value2: "value 2"
}
}
this.state.selected = {
selectedValue: "Selected value is: " + this.state.values1.value1
}
}
And sample
I'm trying to work with a checkbox tree component like this: https://www.npmjs.com/package/react-checkbox-tree, except I'm storing the items that I have selected in Redux. Moreover, the only items that I'm actually storing are the leaf nodes in the tree. So for example, I'd have the full options data which would be used to render the tree:
const fam = {
cuz2: {
name: 'cuz2',
children: {
cuzKid2: {
name: 'cuzKid2',
children: {
}
}
}
},
grandpa: {
name: 'grandpa',
children: {
dad: {
name: 'dad',
children: {
me: {
name: 'me',
children: {}
},
sis: {
name: 'sis',
children: {}
}
}
},
aunt: {
name: 'aunt',
children: {
cuz: {
name: 'cuz',
children: {
name: 'cuzkid',
children: {}
}
}
}
}
}
}
and a separate object that stores the items selected. The following would be the only items that would appear if every checkbox was checked:
const selected = {
cuz2: true,
me: true,
sis: true,
cuz: true
}
I seem to be struggling with this method for having the UI determine which boxes to have fully, partially, or un-checked based on the selected object. I was wondering if anyone can recommend another strategy of accomplishing this.
So I have used react-checkbox-tree but I have customised a bit the icons in order to use another icons library.
Check my example on sandbox:
The library provides a basic example of how to render a tree with selected and/or expanded nodes.
All you need to do is:
set up the nodes with a unique 'value'
Choose which items should be selected (it may comes from Redux)
pass nodes & checked list to the CheckBox constructor
also be sure that when user select/unselect, you update the UI properly using the state
Your code should look similar to this:
import React from 'react';
import CheckboxTree from 'react-checkbox-tree';
const nodes = [{
value: '/cuz2',
label: 'cuz2',
children: [],
},
// other nodes
];
class BasicExample extends React.Component {
state = {
checked: [
'/cuz2'
],
expanded: [
'/cuz2',
],
};
constructor(props) {
super(props);
this.onCheck = this.onCheck.bind(this);
this.onExpand = this.onExpand.bind(this);
}
onCheck(checked) {
this.setState({
checked
});
}
onExpand(expanded) {
this.setState({
expanded
});
}
render() {
const {
checked,
expanded
} = this.state;
return (<
CheckboxTree checked={
checked
}
expanded={
expanded
}
nodes={
nodes
}
onCheck={
this.onCheck
}
onExpand={
this.onExpand
}
/>
);
}
}
export default BasicExample;