so I'm working on an app in which I receive an array from getDerivedStateFromProps, and with that set the state of list, this is an example of the array:
const data = [
[
{
"exerciseName": {
"exerciseName": "Barbell Bench Press",
},
"key": 0.4867576438357962,
"numOfSets": 1,
"paddingBottom": 30,
"reps": "",
"reps2": "",
"reps3": "",
"reps4": "",
"sets": "",
"sets2": "",
"sets3": "",
"sets4": "",
"weigth": "",
"weigth2": "",
"weigth3": "",
"weigth4": "",
},
],
]
as you can see is a nested array and that's probably why it doesn't let me display it in the Flatlist. This is the function I use to get the list:
class mondayExercises extends Component {
constructor(props) {
super(props);
this.state = {
list: [],
}; }
static getDerivedStateFromProps(props, state) {
if (props?.list) {
const mergedArray = [].concat.apply([], props.list);
const flatten = mergedArray.flat(1);
return {
list: [...state.list, flatten],
};
}
return null;
}
I already managed to merge the arrays together, but is there a way to flatten it without changing the entire code of the app?
Related
I am trying to build a folder tree component where initially the component will only render the names of items at the first layer (item1, item2, item3), then when an specific item is clicked, it would show me everything listed on the next layer for that item(i.e. click on item1 would show item1.1 and item1.2, click on item1.1 will show item1.1.1). This should be done in a loop until it reaches the final layer with the just the attributes (attr1, attr2, attr3).
Note: I have simplified the item names for demo purposes, but in reality they don't follow a specific pattern/naming system.
import React, {useState, useEffect} from 'react';
const Testing = () => {
const [expand, setExpand] = useState(false);
const data = {
"item1": {
"item1.1": {
"item1.1.1": {
"item1.1.1.1": {
"attr1": [],
"attr2": "",
"attr3": []
}
}
},
"item1.2": {
"item1.2.1": {
"item1.2.1.1": {
"attr1": [],
"attr2": "",
"attr3": []
}
}
}
},
"item2": {
"item2.1": {
"item2.1.1": {
"item2.1.1.1": {
"attr1": [],
"attr2": "",
"attr3": []
}
},
"item2.1.2": {
"item2.1.2.1": {
"attr1": [],
"attr2": "",
"attr3": []
},
"item2.1.2.2": {
"attr1": [],
"attr2": "",
"attr3": []
}
}
}
},
"item3": {
"item3.1": {
"item3.1.1": {
"item3.1.1.1": {
"attr1": [],
"attr2": "",
"attr3": []
}
},
"item3.1.2": {
"attr1": [],
"attr2": "",
"attr3": []
}
}
}
}
function parse(data) {
if (typeof data === 'object') {
return (
<li>
<ul>
{Object.entries(data).map(([key, value]) => (
<>
<li>
{key}: {typeof value === 'string' ? value : ''}
</li>
{parse(value)}
</>
))}
</ul>
</li>
);
}
if (typeof data === 'object') return (<li>{data}</li>);
return null;
}
return (
<div>
<div className="App">
{Object.entries(data).map(([key, value]) => {
return (
<ul>
<span onClick={() => setExpand(!expand)}>{key}</span>
<br />
<div style={{ display: expand ? "block" : "none", paddingLeft: 15 }}>
{parse(value)}
</div>
</ul>
);
})}
</div>
</div>
)
}
export default Testing;
Here a picture of the output I'm getting right now with the code above: current output; it shows item1, item2, item3 in a list; but when I click on any of them, it will just render everything else, I'm trying to find a way so that it behaves similar to a file directory tree as shown in the demo here: https://codesandbox.io/s/folder-structuring-t6oj4
The issue is that in the demo they use a recursive component that has its own expand state managed with a hook. So every Folder will have a distinct value for the expand variable that is inside their scope.
On the other hand, your Testing component manages a single expand state and the whole rendering uses that state for every "folder" that's the reason why when you click a folder it toggles the state for the whole component, what you should do is refactor your component so that it also manages its own state in each Folder, something like this which is in the demo:
import React, { useState } from "react";
function Folder ({ name, file }) {
const [expand, setExpand] = useState(false);
if (typeof file !== "object") return <span>{file}</span>;
return (
<div>
<span onClick={() => setExpand(!expand)}>{name}</span>
<br/>
<div style={{ display: expand ? "block" : "none", paddingLeft: 15 }} >
{
Object.entries(file).map(([key, value]) => (
<Folder key={key} name={key} file={value} />
))
}
</div>
</div>
);
}
And then just call it in your component:
import React from 'react';
const Testing = () => {
const data = {
"item1": {
"item1.1": {
"item1.1.1": {
"item1.1.1.1": {
"attr1": [],
"attr2": "",
"attr3": []
}
}
},
"item1.2": {
"item1.2.1": {
"item1.2.1.1": {
"attr1": [],
"attr2": "",
"attr3": []
}
}
}
},
"item2": {
"item2.1": {
"item2.1.1": {
"item2.1.1.1": {
"attr1": [],
"attr2": "",
"attr3": []
}
},
"item2.1.2": {
"item2.1.2.1": {
"attr1": [],
"attr2": "",
"attr3": []
},
"item2.1.2.2": {
"attr1": [],
"attr2": "",
"attr3": []
}
}
}
},
"item3": {
"item3.1": {
"item3.1.1": {
"item3.1.1.1": {
"attr1": [],
"attr2": "",
"attr3": []
}
},
"item3.1.2": {
"attr1": [],
"attr2": "",
"attr3": []
}
}
}
}
return (
<div>
<div className="App">
<Folder name="/root" file={data} />
</div>
</div>
)
}
Edit: Refactored the code so that only the selected folder expands on click
I'm having hard time in success to iterate over my external json file in Vue.
I'm importing the file like this:
import json from '../../public/platform.json'
export default {
data: () => ({
currentPage: 0,
brand: '',
platform: '',
affiliate: '',
myJson: json,
}),
Json file looking like this:
{
"Example": {
"Username": "",
"Password": "",
"AffiliateID": "",
"GI": "",
"CI": "",
"freeTextArea": ""
},
"ExampleTwo": {
"Username": "",
"Password": "",
"freeTextArea": ""
}
}
My goal is to do as follows:
I want to check if the "platform" from data is matching "Example" or "ExampleTwo" and if it does, I want to access the fields within either of them.
How can I do it?
You can use a computed property as follows:
computed: {
myPlatform: function () { return json[this.platform] || {}; },
}
Here is a demo: https://codesandbox.io/s/clever-gould-3hkbl?fontsize=14&hidenavigation=1&theme=dark
I want to structure the data that I get from a server, so I can use the TreeView component from Material UI: https://material-ui.com/api/tree-view/
I'm fetching large amounts of data so I want to fetch child nodes from the server when the user clicks on the expand button. So
when the first node is expanded a HTTP request is sent to a server which returns all of the children of that node. When another node is expanded the children of that node is fetched etc.
On startup of the page I want to fetch the root node and its children. The JSON returned will look something like this:
{
"division": {
"id": "1234",
"name": "Teest",
"address": "Oslo"
},
"children": [
{
"id": "3321",
"parentId": "1234",
"name": "Marketing",
"address": "homestreet"
},
{
"id": "3323",
"parentId": "1234",
"name": "Development",
"address": "homestreet"
}
]
}
When expanding the Marketing node I want to make a HTTP call to fetch the children of this node. So I would get JSON like this:
{
"children": [
{
"id": "2212",
"parentId": "3321",
"name": "R&D",
"address": "homestreet"
},
{
"id": "4212",
"parentId": "3321",
"name": "Testing",
"address": "homestreet"
}
]
}
But I am confused on how to create such a data structure which can later be used my the TreeView component. How can I create such a structure?
For anyone still looking for a solution to this problem I've recently tackled it using a combination of the selected and expanded props in the TreeView API. See this Code Sandbox demo for an example of how to asynchronously load new children and expand their parent once they are loaded.
import React from "react";
import TreeView from "#material-ui/lab/TreeView";
import ExpandMoreIcon from "#material-ui/icons/ExpandMore";
import ChevronRightIcon from "#material-ui/icons/ChevronRight";
import TreeItem from "#material-ui/lab/TreeItem";
import TreeNode from "./TreeNode";
const mockApiCall = async () => {
return new Promise((resolve) => {
setTimeout(() => {
const nextId = Math.ceil(Math.random() * 100);
resolve([
{
id: `${nextId}`,
name: `child-${nextId}`,
children: []
},
{
id: `${nextId + 1}`,
name: `child-${nextId + 1}`,
children: []
}
]);
}, Math.ceil(Math.random() * 1000));
});
};
export default class Demo extends React.Component {
constructor(props) {
super(props);
this.state = {
expanded: [],
selected: "1",
tree: new TreeNode({
id: "1",
name: "src",
children: []
})
};
}
handleChange = async (event, nodeId) => {
const node = this.state.tree.search(nodeId);
if (node && !node.children.length) {
mockApiCall()
.then((result) => {
this.setState({ tree: this.state.tree.addChildren(result, nodeId) });
})
.catch((err) => console.error(err))
.finally(() => {
this.setState({
selected: nodeId,
expanded: [...this.state.expanded, nodeId]
});
});
}
};
createItemsFromTree = (tree) => {
if (tree.children.length) {
return (
<TreeItem key={tree.id} nodeId={tree.id} label={tree.name}>
{tree.children.length > 0 &&
tree.children.map((child) => this.createItemsFromTree(child))}
</TreeItem>
);
}
return <TreeItem key={tree.id} nodeId={tree.id} label={tree.name} />;
};
render() {
return (
<TreeView
defaultCollapseIcon={<ExpandMoreIcon />}
defaultExpandIcon={<ChevronRightIcon />}
selected={this.state.selected}
onNodeSelect={this.handleChange}
expanded={this.state.expanded}
>
{this.createItemsFromTree(this.state.tree)}
</TreeView>
);
}
}
I have this db.json and I want to migrate to file. How do I implement it? I had tried this way above and it didn't work for me. When I put the file in src folder, there appears an error message:
Module not found: Can't resolve 'houses.json' in 'C:\Users\user\Desktop\dir\src'
It does the same thing when I put the file in the public folder.
It is my db.json file:
{ "houses": [
{
"id": 1,
"name": "Title here",
"text": "Text here",
"photos": [
"pic1",
"pic2",
"pic3"
]
},
"houses": [
{
"id": 2,
"name": "Title here",
"text": "Text here",
"photos": [
"pic1",
"pic2",
"pic3"
]
},
"houses": [
{
"id": 3,
"name": "Title here",
"text": "Text here",
"photos": [
"pic1",
"pic2",
"pic3"
]
}
}
I had traded this line
const URL = 'http://localhost:3001/houses';
for this other
const URL = require('houses.json');
And it caused the error message showed before.
And how do I fetch these datas from axios? I was fetching datas from json-server doing this way below. It was successfully. I want to do the same thing but not from json-server but using .json file.
const URL = 'http://localhost:3001/houses';
class House extends Component {
constructor(props) {
super(props);
this.state = {
index: 0,
totalHouses: [],
currentHouse: [],
name: [],
photos: [],
text: []
};
}
componentDidMount() {
axios.get(URL)
.then(res => {
this.setState({
totalHouses: Object.keys(res.data),
currentHouse: res.data
})
})
}
//...rest of the code omitted
Here's a simple example how to achieve what you need, while preserving your state approach.
const houses = require('./houses.json');
class House extends Component {
constructor(props) {
super(props);
this.state = {
index: 0,
totalHouses: [],
currentHouse: [],
name: [],
photos: [],
text: []
};
}
componentDidMount() {
this.setState({
totalHouses: Object.keys(houses.data),
currentHouse: houses.data
})
}
//...
}
You can also achieve it without componentDidMount:
const houses = require('./houses.json');
class House extends Component {
constructor(props) {
super(props);
this.state = {
index: 0,
totalHouses: Object.keys(houses.data),
currentHouse: houses.data
name: [],
photos: [],
text: []
};
}
//...
}
It should be require('./houses.json');. Right now it looks for this file inside node_modules or even higher.
I have an object literal or like a json file which has data in it, what I want is to wrap the SMBPremium and MAX data inside an array so that I can target by accessing its index. How Can I modify my object literal to an array so that I can target SMBPremium and MAX. Kinda like this.
productMap['TEC0'][1].productName;
This is my json
var productMap = {
"TEC0": {
"SMBPremium" : {
"productName": "Wilson",
"panelClass": "WilsonClass",
"fullinfoClass": "Wilsonfull",
"productPageLink": "",
"panelPageLinkGA": "",
"fullPageLinkGA": "",
"notifLinkDanger" : {
"linkPrimary" : "",
"linkSecondary" : ""
},
"notifLinkRed" : {
"linkPrimary" : "",
"linkSecondary" : ""
},
"notifLinkInfo" : "",
"notifLinkWarning" : "",
"notifLinkSuccess" : ""
},
"MAX": {
"productName": "Spalding",
"panelClass": "spalding",
"fullinfoClass": "spalding",
"productPageLink": "",
"panelPageLinkGA": "",
"fullPageLinkGA": "",
"notifLinkDanger" : {
"linkPrimary" : "",
"linkSecondary" : ""
},
"notifLinkRed" : {
"linkPrimary" : "",
"linkSecondary" : ""
},
"notifLinkInfo" : "",
"notifLinkWarning" : "",
"notifLinkSuccess" : ""
}
}
};
Tranform with array.map:
productMap2 = {};
productMap2.TEC0 = Object.keys(productMap['TEC0']).map(key => productMap['TEC0'][key]);
Then you can access productName property for each element:
productMap2.TEC0[1].productName
You can recreate the object productMap formatted like you wish:
// Every object is a map:
var tec0 = productMap['TEC0'];
var keys = Object.keys(tec0);
var array = [];
for(var i=0; i<keys.length; i++) {
var key = keys[i];
var value = tec0[key];
array.push(value);
}
var newProductMap = {'TEC0': array};
alert(newProductMap['TEC0'][1].productName);
NOTE: Faly's answer is far more elegant. Just be carefull at browser compatibility with arrow functions (IE does not support for example).
Idem for Ammar's answer, not supported by IE.
Use Object.values() method:
productMap["TEC0"] = Object.values(productMap["TEC0"]);
try this
index = 1
productMap['TEC0'][Object.keys(productMap['TEC0'])[index]].productName;
Explanation
productMap['TEC0'] is an json object
Object.keys(productMap['TEC0']) - will return json object keys as array.
in this example like this ["SMBPremium", "MAX"]
Object.keys(productMap['TEC0'])[index] - will return key name based
on index passed.
productMap['TEC0'][key_name] - will fetch json object based
key_name got from previous state.
You could map the items to an index of the wanted keys array.
var productMap = { TEC0: { SMBPremium: { productName: "Wilson", panelClass: "WilsonClass", fullinfoClass: "Wilsonfull", productPageLink: "", panelPageLinkGA: "", fullPageLinkGA: "", notifLinkDanger: { linkPrimary: "", linkSecondary: "" }, notifLinkRed: { linkPrimary: "", linkSecondary: "" }, notifLinkInfo: "", notifLinkWarning: "", notifLinkSuccess: "" }, MAX: { productName: "Spalding", panelClass: "spalding", fullinfoClass: "spalding", productPageLink: "", panelPageLinkGA: "", fullPageLinkGA: "", notifLinkDanger: { linkPrimary: "", linkSecondary: "" }, notifLinkRed: { linkPrimary: "", linkSecondary: "" }, notifLinkInfo: "", notifLinkWarning: "", notifLinkSuccess: "" } } },
keys = ['SMBPremium', 'MAX'];
keys.forEach((k, i) => productMap.TEC0[i] = productMap.TEC0[k]);
console.log(productMap['TEC0'][0].productName);