How do I pass children index number to a react component? - javascript

My Data looks like the below.
const data = {
main: {
88: {
issues: [
{
id: 1
},
{
id: 3
},
{
id: 4
}
]
}
}
};
I am looping through data.main and then passing some data to the child component like this.
{Object.values(data.main).map((key) => {
<Issues
id={key}
issueIndex={XXX}
{...this.props}
/>
}
But I also want to pass an index of all issues to the child element. So that I can number it from here.
My attemp inside jsx file below.
{Object.values(data.main).map((group, key) => {
let issuesArr = group.issues;
{issuesArr.map((value, index) => { index + 1; })}
<Issue
id={key}
issueIndex={XXX}
{...this.props}
/>
}
I want to pass a number as issueIndex to the <Issue /> I have no control of the data structure.

That is happening because your issuesArr.map isn't doing anything. Change your code to this:
{
Object.values(data.main).map((group, key) => {
let issuesArr = group.issues;
{
issuesArr.map((value, index) => {
return (
<Issue
id={key}
issueIndex={value.id}
{...this.props}
/>
)
}
}
}

Related

dynamically set the props of React Components

I've an object of props values where the key is the name of the component and the value is the prop's value
{
"ComponentA.prop1": 23,
"ComponentA.prop2": 100,
"ComponentB.prop3": 200,
}
then I've all these components inside a form
<form>
<ComponentA />
<ComponentB />
</form>
How can I dynamically pass all the defined props to the correct components elegantly.
You can try this if it works with your use case
const obj = {
"ComponentA.prop1": 23,
"ComponentA.prop2": 100,
"ComponentB.prop3": 200
};
const getProps = (name) => {
let nObj = {};
Object.keys(obj)
.filter((key) => key.split(".").shift() === name)
.forEach((key) => {
let nKey = key.split(".").pop();
nObj[nKey] = obj[key];
});
return nObj;
};
<form>
<ComponentA {...getProps('ComponentA')} />
<ComponentB {...getProps('ComponentB')} />
</form>
Why not change the structure of your data?
const data = [
{
component: ComponentA,
props: {
...componentAprops,
},
},
{
component: ComponentB,
props: {
...componentBprops,
},
},
{
component: ComponentC,
props: {
...componentCprops,
},
},
];
Then you can simply map these into ui components
const elements = data.map(d => {
const Component = d.component;
return <Component {...d.props} />;
});

Trying to update props in state with method in class component

I'm trying to render a closable tab bar using some Material UI components, and I'm having trouble implementing the onDelete method for when the user wants to close a tab. I'm passing the data set, an array of objects, as a prop called dataSet. I want to update it whenever the user closes a tab but it doesn't re-render; all tabs still appear. When I console.log this.state.dataSet on each click however, I see that the tabs are getting deleted. What am I doing wrong?
class ClosableTabs extends Component {
state = {
tabIndex: 0,
dataSet: this.props.dataSet,
};
onDelete = id => {
this.setState(prevState => {
const updatedDataSet = prevState.dataSet.filter(tab => tab.id !== id);
return {
dataSet: updatedDataSet,
};
}, console.log(this.state.dataSet);
};
renderTabs = dataSet => {
return dataSet.map(data => {
return (
<Tab
key={data.id}
label={
<span>
{data.title}
</span>
<Button
icon="close"
onClick={() => this.onDelete(data.id)}
/>
}
/>
);
});
};
render() {
const { value, dataSet, ...rest } = this.props;
return (
<TabBar value={this.state.tabIndex} onChange={this.onChange} {...rest}>
{this.renderTabs(dataSet)}
</TabBar>
);
}
}
export default Tabs;
and here is my data set that I pass as props when I use <ClosableTabs />
const dataSet = [
{
id: 1,
title: 'title 1',
},
{
id: 2,
title: 'title 2',
},
{
id: 3,
title: 'title 3',
},
];
When you render dataSet, you use the array you get from props (which never changes):
render() {
const { value, dataSet, ...rest } = this.props; // dataSet comes from props
return (
<TabBar value={this.state.tabIndex} onChange={this.onChange} {...rest}>
{this.renderTabs(dataSet)} // renderTabs renders this.props.dataSet
</TabBar>
);
}
}
instead, render dataSet which comes from your state (you should use different naming for this.props.dataSet and this.state.dataSet to avoid this kind of mistakes):
render() {
const { value, ...rest } = this.props;
const { dataSet } = this.state; // dataSet now comes from state
return (
<TabBar value={this.state.tabIndex} onChange={this.onChange} {...rest}>
{this.renderTabs(dataSet)} // renderTabs renders this.state.dataSet
</TabBar>
);
}
}
The problem is you are rendering the component with props instead of state.
Your render function should look likes this:
render() {
const { value, dataSet, ...rest } = this.props;
return (
<TabBar value={this.state.tabIndex} onChange={this.onChange} {...rest}>
{this.renderTabs(this.state.dataSet)}
</TabBar>
);
}
}

React map over the array object

I'm quite new with react stuff, what I am trying is to generate some dynamic stuff using .map()
This is my component:
import React, { Component } from "react";
class DynamicStuff extends Component {
state = {
options: [
{ id: 1, optionOne: "OptionOne" },
{ id: 2, optionTwo: "OptionTwo" },
{ id: 3, optionThree: "OptionThree" }
]
};
render() {
const options = [...this.state.options];
return (
<>
{options.map((option) => {
return {option}
})}
<span>{options.optionOne}</span>
<span>{options.optionTwo}</span>
<span>{options.optionThree}</span>
</>
);
}
}
export default DynamicStuff;
What I am doing wrong here and why the map is not generating expected result ?
Is it ok?
import React, { Component } from "react";
class DynamicStuff extends Component {
state = {
options: [
{ id: 1, value: "OptionOne" },
{ id: 2, value: "OptionTwo" },
{ id: 3, value: "OptionThree" }
]
};
render() {
const options = [...this.state.options];
return (
<>
{options.map((option) => {
return <span>{option.value}</span>
})}
</>
);
}
}
export default DynamicStuff;
You have made your options object incorrectly. We need to have a same attribute over all the objects in the array.
class App extends React.Component {
state = {
options: [
{ id: 1, option: "OptionOne" },
{ id: 2, option: "OptionTwo" },
{ id: 3, option: "OptionThree" }
]
};
render() {
const options = [...this.state.options];
return (
<>
{options.map((option, index) => (
<li key={index}>{option.option}</li>
))}
</>
);
}
}
Another thing, If you need to map an array. You don't need to have many spans. Having a one is just enough. The map function will iterate and give you all the things.
The map used here is actually to convert the js object into a react element, but your usage here is still a js object after the map conversion. The react element may be a <p key = {option.id}> {option. optionOne} </p>.
If there is a react element after the return in your map, it is correct.
{options.map((option) => {
return <p key = {option.id}> {option. optionOne} </p>
})}
or
{options.map((option) => <p key = {option.id}> {option. optionOne} </p>)}
YOu need to map and return the HTML element
return ({
options.map((option) => {
return `<span key={option.id}>${option. option}</span>`
})
});
You should do something like
render() {
const { options } = this.state;
return (
<div className="option-wrapper">
{options.length > 0 && options.map(option => {
return <span key={option.id}>{option.option}</span>
})}
</div>
);
}

React Native add Screens dynamically

I want to add screens dynamically from a changeable array.
This works:
but this doesn't work:
It says: Error: Couldn't find any screens for the navigator. Have you defined any screens as its children?
How can I fix the second to be more "dynamic"?
you can change your components to list of objects.
then loop inside the item by their key and value.
const components = [
{
name: "Home",
element: HomeScreen
},
{
name: "Details",
element: DetailsScreen
}
];
const renderScreen = () => {
let result = null;
if (components) {
result = components.map((item, i) => {
return <Stack.Screen name={item.name} element={item.element} />
});
}
return result;
}
return(
{renderScreen()}
)
change element => { <Stack... /> } to element => { return <Stack... /> } or element => ( <Stack... /> )

ReactJS: Return multiple list elements without wrapping element

This is how I'm building a simple list with react:
render() {
return (
<List>
{
result.map((i, index) => {
return (
<List.Item>
{ i.title }
</List.Item>
)
})
}
</List>
)
}
But if there is a children array, I need those elements also added to the returned list.
result.map((i, index) => {
if (i.children) {
// return i.title and also title of children elements
}
return (
<List.Item>
{ i.title }
</List.Item>
)
})
Example
Assume i looks like this:
[
{ title: 'one' },
{ title: 'two', children: [ { title: 'foo' }, { title: 'bar' } ] }
]
I need this result and I need to avoid a wrapping div element:
<List>
<List.Item>one</List.Item>
<List.Item>two</List.Item>
<List.Item>foo</List.Item>
<List.Item>bar</List.Item>
</List>
Simple recursion will do the job
Try
parseResult(list, isParent) {
var result = list.map(i => {
// If there's a child, call the function again to parse it
if (Array.isArray(i.children)) {
//First render parent, then all child You need to wrap them in something
return <div key={i.title}>
<Label className="default-label" name={i.title} onClick={this.parentEvent} />{this.parseResult(i.children)}
</div>
}
if (isParent)
return <Label key={i.title} className="default-label" name={i.title} onClick={this.parentEvent} />
else
return <Label key={i.title} className="default-label" name={i.title} data-child="something" onClick={this.childEvent} />
})
return result
}
And in your render method(change list with you variable)
{this.parseResult(list, true)}
Also make sure you have childEvent() and parentEvent() defined.
If you just want expand collapse feature, why not use existing libraries! Here's a good resource https://react.rocks/tag/Expand_Collapse
Not as elegant as using recursion but slightly easier to follow. If the item contains children push the children to the result array.
const items = [
{ title: 'one'},
{ title: 'two', children: [ { title: 'foo' }, { title: 'bar' } ] },
{ title: 'three'}
];
const result = [];
for (var i = 0; i < items.length; i ++) {
result.push(items[i].title);
if (items[i].children) {
for (var k = 0; k < items[i].children.length; k ++) {
result.push(items[i].children[k].title)
}
}
}
console.log(result);

Categories

Resources