Avoid looping multiple times the same array in React and JSX - html

Suppose I have this array of objects:
const itensArray = [
{ "year": 2000, "text": "Lorem ipsum" },
{ "year": 2010, "text": "Hello World" },
{ "year": 2020, "text": "This is the end" }
];
Suppose it will be used to create elements in an timeline HTML structure, where the elements are separeted:
<div className="timeline-years">
<ul>{ yearsList }</ul>
</div>
<div className="timeline-texts">
<ul>{ textList }</ul>
</div>
I know one way to achieve this is to loop the same array two times:
const yearsList = itensArray.map(item =>
<li>{ item.year }</li>
);
const textList = itensArray.map(item =>
<li>{ item.text }</li>
);
How can I achieve the same result in one map only and using React and JSX?
The code below is wrong, but it illustrates what I want:
itensArray.map(item =>
let yearsList = <li>{ item.year }</li>
let textList = <li>{ item.text }</li>
);
Thanks!

If your target is to achieve this using only one loop then it could be a solution.
export default function App() {
const itensArray = [
{ "year": 2000, "text": "Lorem ipsum" },
{ "year": 2010, "text": "Hello World" },
{ "year": 2020, "text": "This is the end" }
];
const yearsList = []
const textList = []
itensArray.forEach((item)=>{
yearsList.push(<li>{ item.year }</li>)
textList.push(<li>{ item.text }</li>)
})
console.log(yearsList)
return (
<div className="App">
<ul>{yearsList}</ul>
<ul>{textList}</ul>
</div>
);
}

Dont afraid loop twice over one array, because data you should display in different places. You may preloop your array and assign your bunch of li elements as a const and put them in react fragments:
import React from 'react';
const itensArray = [
{ year: 2000, text: 'Lorem ipsum' },
{ year: 2010, text: 'Hello World' },
{ year: 2020, text: 'This is the end' },
];
function Component() {
const yearsList = (
<>
{itensArray.map((item) => (
<li>{item.year}</li>
))}
</>
);
const textList = (
<>
{itensArray.map((item) => (
<li>{item.text}</li>
))}
</>
);
return (
<>
<div className="timeline-years">
<ul>{yearsList}</ul>
</div>
<div className="timeline-texts">
<ul>{textList}</ul>
</div>
</>
);
}
export default Component;

Related

React JSON is undefined

I'm fetching this JSON from an API:
{
"data": {
"email": "test#tre.com",
"inserted_at": "2021-03-30T15:37:06",
"links": [
{
"id": 1,
"title": "My link title",
"url": "http://google.com"
},
{
"id": 2,
"title": "My Youube title",
"url": "http://youtube.com"
}
]
}
}
I'm fetching it this way using Hooks:
export default function Notes() {
const [json, setJSON] = useState([]);
useEffect(() => {
fetch("http://localhost:4000/api/users/1", {
method: "GET"
})
.then((response) => response.json())
.then((json) => {
// console.log(data);
setJSON(json);
})
.catch((err) => {
console.error(err);
});
}, [setJSON]);
Then I try to show it like this:
return (
<>
<div className="content">
{JSON.stringify(json)}
<h1>{json.email}</h1>
</div>
</>
);
The line {JSON.stringify(json)} shows the JSON.
But the line <h1>{json.email}</h1> doesn't show anything.
I don't know why that happens and how can I access my variables.
Thanks . I appreciate any help
Is the data in the form of an array or an object?
You defined the initial state as and array ad hence you cannot do
// you can't do json.email if you expect the response as and array
const [json, setJSON] = useState([]);
change it to
const [json, setJSON] = useState({});
if it is an object. Then in the template do
{json.data && <h1>{json.data.email}</h1>}
<h1>{json.data && json.data.email}</h1>
instead of
<h1>{json.email}</h1>

Best way for maping html elements from material ui and rendering by map

<FormControlLabel
control={
<Switch
onChange={(e) => changeSwitchState(e.target.name, e.target.checked)}
color='primary'
name='a'
/>
}
label="A"
/>
<FormControlLabel
control={
<Switch
onChange={(e) => changeSwitchState(e.target.name, e.target.checked)}
color='primary'
name='b'
/>
}
label="B"
/>
<FormControlLabel
control={
<Switch
onChange={(e) => changeSwitchState(e.target.name, e.target.checked)}
color='primary'
name='c'
/>
} label="C" />
<span>D</span>
<InputText
className={inputValidation(inputValues.e, inputValues.f, inputValues.d) ? 'err' : 'validated'}
id='d'
keyfilter="num"
value={inputValues.d}
onChange={e => changeInputValue(e.target.id, e.target.value)}
/>
<span>E</span>
<InputText
className={inputValidation(inputValues.f, inputValues.d, inputValues.e) ? 'errE' : 'validateE'}
id='e'
value={inputValues.e}
onChange={e => changeInputValue(e.target.id, e.target.value)}
mode="decimal"
useGrouping={false}
/>
)
};
here is mine code i want make code shorter and render this inputs and switch buttons by map way can someone explain how to do this and what is best practice and how to noth lost data which one im reciving by props way ?
Best way would to be to setup configuration arrays for this. Ideally you would want to keep your inputs in the "Controlled State" in React so your user interface always represents your state.
Lets first configure a constant which holds the initial configuration of your formControlLabels, which holds information I can read from your code which you supplied.
It might look something like this, and can be defined outside of the component which uses it. Arrays holding objects for each input is ideal, since later we want to use map to render these in your return method.
const formControlLabelConfig = [
{
color: "primary",
name: "a",
label: "A",
state: false
},
{
color: "primary",
name: "b",
label: "B",
state: false
},
{
color: "primary",
name: "c",
label: "C",
state: false
}
];
similiarly for your textInput components
const textInputConfig = [
{
keyFilter: "num",
id: "d",
mode: undefined,
className: "err",
errorClassName: "validated",
useGrouping: undefined,
value: ""
},
{
keyFilter: "num",
id: "e",
mode: "decimal",
className: "errE",
errorClassName: "validateE",
useGrouping: false,
value: ""
}
];
We can setup a state variable using this initial configuration. This would be within the functional component you are using to render the FormControlLabel and InputText components
const [formControlLabelState, setFormControlLabelState] = useState(formControlLabelConfig);
const [textInputConfig, setTextInputConfig] = useState(textInputConfig);
we can then use map to render each component according to its config. I have mocked up something of what you will end up with
import React, { useState } from "react";
const formControlLabelConfig = [
{
color: "primary",
name: "a",
label: "A",
state: false
},
{
color: "primary",
name: "b",
label: "B",
state: false
},
{
color: "primary",
name: "c",
label: "C",
state: false
}
];
const textInputConfig = [
{
keyFilter: "num",
id: "d",
mode: undefined,
className: "err",
errorClassName: "validated",
useGrouping: undefined,
value: ""
},
{
keyFilter: "num",
id: "e",
mode: "decimal",
className: "errE",
errorClassName: "validateE",
useGrouping: false,
value: ""
}
];
const SomeComponentName = () => {
const [formControlLabelState, setFormControlLabelState] = useState(
formControlLabelConfig
);
const [textInputState, setTextInputState] = useState(textInputConfig);
const getInputClassName = () => {
let className = "";
//return the className on validation
return className;
};
const changeInputValue = (id, value) => {
setTextInputState((prevState) => {
const newState = [...prevState];
const index = newState.findIndex((config) => config.id === id);
newState[index].value = value;
return newState;
});
};
const changeSwitchState = (name, chackedState) => {
setFormControlLabelState((prevState) => {
const newState = [...prevState];
const index = newState.findIndex((config) => config.name === name);
newState[index].state = chackedState;
return newState;
});
};
return (
<>
{formControlLabelState.map((config) => (
<FormControlLabel
key={config.id}
control={
<Switch
onChange={(e) =>
changeSwitchState(e.target.name, e.target.checked)
}
color={config.color}
name={config.name}
value={config.checked} //would need to check this. Not sure what attribute is used to set the checked state
/>
}
label="B"
/>
))}
<span>D</span>
{textInputState.map((config) => (
<InputText
key={config.id}
className={() => getInputClassName(config.id)}
id={config.id}
value={config.value}
onChange={(e) => changeInputValue(e.target.id, e.target.value)}
mode={config.mode}
useGrouping={config.useGrouping}
/>
))}
</>
);
};

Recursive rendering of JSON tree with HTML elements (Input, Radio, etc.)

I am quite new to React and working with JSON structures. I am trying to construct a recursive render of a JSON tree structure that dynamically renders individual HTML elements (e.g. radio buttons, dropdown menus, etc.) from the tree. I have seen other implementations, but they do not have nested HTML elements that differ from li, ul, etc. They also do not typically have different naming conventions further down the tree (e.g. attributes, options).
The tree looks like this:
{
"id": "1",
"name": "Animals",
"color": "#e37939",
"shape": "bounding_box",
"attributes": [
{
"id": "1.1",
"name": "Type",
"type": "radio",
"required": false,
"options": [
{
"id": "1.1.1",
"optionName": "Cats",
"optionValue": "cats",
"options": [.... and so on
};
What I ultimately what to achieve is to get to a format where one clicks the 'Animals button', which then renders the nested radio button, and if one selects the 'cats' option value it'd render the next dropdown menu. I have set up an initial set of methods, but I can't quite figure out how to dynamically render the next set of nested options when an option is clicked. I have created a React fiddle here: https://codesandbox.io/s/vigilant-grothendieck-jknym
The biggest challenge is to get the nested recursive options embedded in an options group. I haven't been able to figure out how to do that yet.
I have created a datastructure for what you want to achieve , altho i have tweaked it a bit as there are parts of it redundant but you can still keep both data structure and convert between them.it goes recursively as deep as you want it to go.
const prodList = [
{
id: "1",
name: "Animals",
options: [
{
id: "1.1",
name: "Type",
inputType: "radio",
options: [
{
id: "1.1.1",
name: "Cats",
value: "Cats",
inputType: "select",
options: [
{ id: "1.1.1.1", name: "Siamese Grey", value: "Siamese Grey" },
{ id: "1.1.1.2", name: "Siamese Black", value: "Siamese Black" },
{ id: "1.1.1.3", name: "Siamese Cute", value: "Siamese Cute" },
{ id: "1.1.1.4", name: "House Cat", value: "House Cat" },
{ id: "1.1.1.5", name: "House Cat", value: "House Cat" }
]
},
{ id: "1.1.2", name: "Dogs", value: "Dogs" },
{ id: "1.1.3", name: "Cows", value: "Cows" }
]
}
]
}
];
above is the data structure where you have "inputType" property that helps determining what component to show. we will have a basic component , a radio component and a select component of each type which can render each other inside them.
export default class ProductsPage extends Component {
render() {
let prodItems = prodList.map(p => {
return <MainContentManager data={p} key={p.id} />;
});
return <div>{prodItems}</div>;
}
}
class MainContentManager extends Component {
render() {
let renderObj = null;
renderObj = basicMethod(renderObj, this.props.data);
return (
<div>
<h6> {this.props.data.name}</h6>
{renderObj}
</div>
);
}
}
class RadioButtonManager extends Component {
constructor(props) {
super(props);
this.state = {
activeOptionIndex: 0
};
this.handleInputClick = this.handleInputClick.bind(this);
}
handleInputClick(index) {
this.setState({
activeOptionIndex: index
});
}
render() {
let renderObj = null;
let renderDat = null;
renderDat = this.props.data.options.map((op, index) => {
return (
<label key={op.id}>
<input
type="radio"
onChange={e => {
this.handleInputClick(index);
}}
checked={index == this.state.activeOptionIndex ? true : false}
/>
{op.name}
</label>
);
});
renderObj = basicMethod(renderObj, {
options: [this.props.data.options[this.state.activeOptionIndex]]
});
return (
<div>
<h6> {this.props.data.name}</h6>
{renderDat}
{renderObj}
</div>
);
}
}
class SelectManager extends Component {
constructor(props) {
super(props);
this.state = { value: "", activeOptionIndex: 0 };
this.handleInputClick = this.handleInputClick.bind(this);
}
handleInputClick(value) {
let activeOptionIndex = this.state.activeOptionIndex;
if (this.props.data.options) {
for (let i = 0; i < this.props.data.options.length; i++) {
if (this.props.data.options[i].value == value) {
activeOptionIndex = i;
}
}
}
this.setState({
value: value,
activeOptionIndex: activeOptionIndex
});
}
render() {
let renderObj = null;
let selectOptions = this.props.data.options.map((op, index) => {
return (
<option key={op.value} value={op.value}>
{op.name}
</option>
);
});
renderObj = basicMethod(renderObj, {
options: [this.props.data.options[this.state.activeOptionIndex]]
});
return (
<div>
<select
onChange={e => {
this.handleInputClick(e.target.value);
}}
>
{selectOptions}
</select>
{renderObj}
</div>
);
}
}
function basicMethod(renderObj, data) {
if (data && data.options) {
renderObj = data.options.map(op => {
!op && console.log(data);
let comp = null;
if (op.inputType == "radio") {
comp = <RadioButtonManager data={op} key={op.id} />;
} else if (op.inputType == "select") {
comp = <SelectManager data={op} key={op.id} />;
} else {
comp = <MainContentManager data={op} key={op.id} />;
}
return comp;
});
}
return renderObj;
}
ask anything if it is unclear or you want it a bit different.

Looping over an object that is within a another object

So I was working on this name card generator app. https://jsonplaceholder.typicode.com/users is the source of my database.
Any idea how to loop through the address part? My code so far.
import React, {useState, useEffect} from 'react';
import Namecard from './namecard'
function App() {
const [identis, setIdenti]=useState([]);
useEffect(()=>{
getIdenti()
},[]
);
const getIdenti = async()=>{
const acquired = await fetch(`https://jsonplaceholder.typicode.com/users`)
const data = await acquired.json()
setIdenti(data)
}
return (
<div>
{identis.map(identi=>(
<Namecard
name={identi.name}
email={identi.email}
address={identi.address}
/>
))}
</div>
)}
export default App
I think maybe you're going for something along these lines. Just to at least answer the question - here is how you would loop through the address. And I'm guessing you're trying to build up a readable string or something...
var users = [
{
"id": 1,
"name": "Leanne Graham",
"username": "Bret",
"email": "Sincere#april.biz",
"address": {
"street": "Kulas Light",
"suite": "Apt. 556",
"city": "Gwenborough",
"zipcode": "92998-3874",
"geo": {
"lat": "-37.3159",
"lng": "81.1496"
}
}
}
];
var addressStr = '';
for (var user of users) {
addressStr = '';
for (var key of Object.keys(user.address)) {
key !== 'geo' && (addressStr += user.address[key] + ' ');
}
console.log(addressStr);
}
instead of looping through address part, just build a separate Address component.
const Address = ({ street, suite, ... }) => <h1>Street: {street}</h1>
const Namecard = ({ name, ..., children }) => <div> Name: {name} <div>{children}</div></div> // where children is the Address component
<Namecard
name={identi.name}
email={identi.email}
>
<Address {...identi.address} />
</Namecard>

Getting json object data with react

I am attempting to pull data out of json like this, which is imported as "values"
{
"content": {
"person": [
{
"name": "Test"
"age" : "24:
}
]
}
}
I am using .map like below but getting the error .default.map is not a function I believe it is because i have objects not arrays, i've tried a bunch of stuff including object.keys but i'm getting errors all over the place, any direction would be appreciated.
import values from './sample.json'
const vals = values.map((myval, index) => {
const items = person.items.map((item, i) => {
return (
<div>{item.name}</div>
)
})
return (
<div>{items}</div>
)
})
I think your data and code have some errors. But after fixing those and also changing the name from 'person' to 'people' if that's what you are after, here's the code that does what you are trying to do:
var data = {
content: {
people: [
{
name: "Test",
age: 24,
},
{
name: "Foo",
age: 25,
},
],
},
};
var App = React.createClass({
render: function () {
var people = data.content.people.map(function (person) {
return <div>{person.name}</div>;
});
return <div>{people}</div>;
},
});
ReactDOM.render(<App />, document.getElementById("app"));
And here's the JSBin for that: https://jsbin.com/coyalec/2/edit?html,js,output
Update: I'm updating the answer with more detailed example. It now deals with data more generically, like it doesn't assume what are the entries of 'contents' and such, but it knows that each type like 'people' or 'pets' are an array.
var data = {
content: {
people: [
{
name: "Test",
age: 24,
},
{
name: "Foo",
age: 25,
},
],
pets: [
{
name: "Sweety",
age: 3,
},
{
name: "Kitty",
age: 5,
},
],
},
};
var App = React.createClass({
render: function () {
// Get the keys in data.content. This will return ['people', 'pets']
var contentKeys = Object.keys(data.content);
// Now start iterating through these keys and use those keys to
// retrieve the underlying arrays and then extract the name field
var allNames = contentKeys.map((t) =>
data.content[t].map((e) => <div>{e.name}</div>)
);
return <div>{allNames}</div>;
},
});
ReactDOM.render(<App />, document.getElementById("app"));
And here's the latest JSBin: https://jsbin.com/coyalec/4/edit?html,js,output