I'm trying to create a SectionList from data received from an API. This API has the following structure for the ingredients that I want to display:
const ingredients = {
malt: [
{
name: 'Maris Otter Extra Pale',
amount: {
value: 3.3,
unit: 'kilograms',
},
},
{
name: 'Caramalt',
amount: {
value: 0.2,
unit: 'kilograms',
},
},
{
name: 'Munich',
amount: {
value: 0.4,
unit: 'kilograms',
},
},
],
hops: [
{
name: 'Fuggles',
amount: {
value: 25,
unit: 'grams',
},
add: 'start',
attribute: 'bitter',
},
{
name: 'First Gold',
amount: {
value: 25,
unit: 'grams',
},
add: 'start',
attribute: 'bitter',
},
{
name: 'Fuggles',
amount: {
value: 37.5,
unit: 'grams',
},
add: 'middle',
attribute: 'flavour',
},
{
name: 'First Gold',
amount: {
value: 37.5,
unit: 'grams',
},
add: 'middle',
attribute: 'flavour',
},
{
name: 'Cascade',
amount: {
value: 37.5,
unit: 'grams',
},
add: 'end',
attribute: 'flavour',
},
],
yeast: 'Wyeast 1056 - American Aleā¢',
};
The desired result is the following using SectionList (with malt and hops as section headers):
Visual example of result
I've already tried to use functions like Object.values(), with no result at all. The code simply looks like the following, receiving the beer from the previous view(with a beer list):
const Detail = ({ route }) => {
const beer = route.params.beer;
const ingredientsFormatted = Object.values(beer.ingredients);
return(
<SectionList
sections={ingredientsFormatted}
renderItem={({ item }) => {
<Text>{item.name}</Text>; //Here there has to be the name of the different ingredients
}}
renderSectionHeader={({ section }) => <Text>{section}</Text>} //Here there has to be malt, hops or yeast
keyExtractor={(item) => item.name}
></SectionList>
)}
There are 2 problems in your ingredients data.
Format in value for yeast is not the same as the others.
Your data inserted into SectionList is not the format recommended by React-Native Offical docs.
To fix this, you can modify your API return / map the data after retrieved from server.
const Detail = ({ route }) => {
const beer = route.params.beer;
let sectionListTemplate = [];
for (let [key, value] of Object.entries(beer.ingredients)) {
//console.log(`${key}: ${JSON.stringify(value)}`);
if(!Array.isArray(value)){
//Handle case for 'yeast' which its data is not an array
sectionListTemplate.push({
title: key,
data: [{
name: value
}]
})
}
else{
//Map value for general cases
sectionListTemplate.push({
title: key,
data: value
})
}
}
return(
<SectionList
sections={sectionListTemplate}
renderSectionHeader={({ section }) => {
return(
//Here there has to be malt, hops or yeast
<Text style={{fontWeight: 'bold'}}>
{section.title}
</Text>
)
}}
renderItem={({ item }) => {
return(
//Here there has to be the name of the different ingredients
<Text>
{item.name}
</Text>
)
}}
keyExtractor={(item) => item.name}
>
</SectionList>
)
)}
Related
index.tsx
data = {
room: [
{
id: 1,
name: 'room1'
},
{
id: 2,
name: 'room2'
},
{
id: 3,
name: 'room3'
}
],
student: [
{
id: 1,
room: 'room1',
name: 'josh'
},
{
id: 2,
room: 'room1',
name: 'jake'
}
]
}
const _ROOM = data['room'];
const _STUDENT = data['student'];
const form = {
config: [
{
label: "Room",
name: "room",
type: "select",
rule: yup.string().required(),
renderer: (data: any) => {
const { control, register, errors } = useFormContext();
return (
<SelectPicker
placeholder="Room"
data={
_ROOM && _ROOM.length > 0 ?
_ROOM.map(x => ({ label: x.name, value: x.id })) : []
}
style={{ width: '100%' }}
onChange={(val) => {
control.setValue('room', val);
}}
value={control.getValues()['room']}
/>
);
}
},
{
label: "Student",
name: "student",
type: "select",
rule: yup.string().required(),
renderer: (data: any) => {
const { control, register, errors } = useFormContext();
return (
<SelectPicker
placeholder="Student"
data={
_STUDENT && _STUDENT.length > 0 ?
_STUDENT.map(x => ({ label: x.name, value: x.id })) : []
}
style={{ width: '100%' }}
onChange={(val) => control.setValue('student', val)}
value={control.getValues()['student']}
/>
);
}
}]}
How to filter the student based on the room. for example I select the room1 then on the student it will filter which it has value room1. I try to filter inside the onchange in room but it doesn't work also not filtering or display the console log. also I used the state then set inside the onChange but it doesn't work also.
Take a look at this: https://react-hook-form.com/advanced-usage/#ConditionalControlledComponent
Basically, you can watch for changes in room and filter out the options in the student field.
const {room} = watch();
And in the SelectPicker, modify the data prop to:
data={
_STUDENT && _STUDENT.length > 0 ?
_STUDENT.filter(s => s.room === room).map(x => ({ label: x.name, value: x.id })) : []
}
I have the following Data which when downloaded can be viewed in an xls/csv format. I am using react-csv npm package which is displaying the name and description in same columns as opposed to different columns.
I need some help in figuring out how to display data where Name, Description, and Roles are displayed in different columns as shown below.
Data:-
const csvData = [
{
name: 'Data science training',
description:
'Data Science certification training',
suggestedRoles: [
{ id: 16, category: 'DEVELOPER', name: 'Data Engineer' },
{ id: 17, category: 'DEVELOPER', name: 'Data Scientist' }]
},{
name: 'AWS',
description:
'AWS certification training',
suggestedRoles: [
{ id: 16, category: 'DEVELOPER', name: 'Cloud Engineer' },
{ id: 17, category: 'DEVELOPER', name: 'Network Engineer' }]
}],
Expected Output:-
Name Description RoleName
Data Science Training Data Science Certification Training Data Engineer
Data Scientist
AWS Training AWS Certification Training Cloud Engineer
Network Engineer
Current Output:-
Name Description RoleName
Data Science Training,AWS Training Data Science Certification Training, AWS Certification Training Data Engineer,Data Scientist,Cloud Engineer,Network Engineer
Code:-
export const ReactCsv = () => {
const createCsvFileName = ()=> `data_${moment().format()}.csv`;
const headers = [
{ label: 'Name', key: 'name' },
{ label: 'Description', key: 'description' },
{ label: 'SuggestedRoles', key: 'suggestedRoles' }
];
const data = [
{
name: csvData.map((_)=>_.name),
description: csvData.map((_)=>_.description),
suggestedRoles: csvData.map((_)=>_.suggestedRoles.map((role)=>role.name)),
}
];
return (
<CSVLink
data={data}
headers={headers}
filename={createCsvFileName()}
target="_blank"
style={{ textDecoration: 'none', outline: 'none', height: '5vh' }}
>
<Button variant="contained" color="secondary" style={{ height: '100%' }}>
Download CSV
</Button>
</CSVLink>
);
};
The data variable must be an array of objects, not an array of a single object where each property is an array of values.
const csvData = [{
name: 'Data science training',
description: 'Data Science certification training',
suggestedRoles: [{
id: 16,
category: 'DEVELOPER',
name: 'Data Engineer'
},
{
id: 17,
category: 'DEVELOPER',
name: 'Data Scientist'
}
]
}, {
name: 'AWS',
description: 'AWS certification training',
suggestedRoles: [{
id: 16,
category: 'DEVELOPER',
name: 'Cloud Engineer'
},
{
id: 17,
category: 'DEVELOPER',
name: 'Network Engineer'
}
]
}];
const data = csvData.map(item => ({
name: item.name,
description: item.description,
suggestedRoles: item.suggestedRoles.map(role => role.name),
}))
console.log(data);
Your ReactCsv's data generating code should be changed into like this one.
export const ReactCsv = () => {
const createCsvFileName = ()=> `data_${moment().format()}.csv`;
const headers = [
{ label: 'Name', key: 'name' },
{ label: 'Description', key: 'description' },
{ label: 'SuggestedRoles', key: 'suggestedRoles' }
];
let data = []
csvData.forEach(item => {
data.push({
name: item.name,
description: item.description,
suggestedRoles: item.suggestedRoles[0].name
});
for (let i = 1; i < item.suggestedRoles.length; i++) {
const role = item.suggestedRoles[i];
data.push({
name: '',
description: '',
suggestedRoles: role.name
});
}
});
return (
<CSVLink
data={data}
headers={headers}
filename={createCsvFileName()}
target="_blank"
style={{ textDecoration: 'none', outline: 'none', height: '5vh' }}
>
<Button variant="contained" color="secondary" style={{ height: '100%' }}>
Download CSV
</Button>
</CSVLink>
);
};
The data property should be one single object. So, what you can do is create a function that returns an object. That object structure you can decide based on the data and attributes you want to display.
eg:
const newBillingReport = () => {
try {
const newBillingReportData = billingExportReportData.map((data) => ({
"Type": data?.type || "Not Provided",
"Status": data?.status || "Not Provided",
"Billed": data?.billedAmount || "0",
}),[])
return newBillingReportData
} catch (error) {
console.error(error)
}
}
This will returns object with three attributes:
eg:
{
"Type": "COMPLETED",
"Status": "DONE",
"Billed": "True"
}
So like this you can create a function. Then for the header attribute map same as you returned. That should also will be a function that returns an object
eg:
const formatUserName = () => {
return [
{ label: "Type", key: "Type" },
{ label: "Status", key: "Status" },
{ label: "Billed ($)", key: "Billed" },
]
}
In here you can see how you need to add label and keys. Most likely the function return data.
So CVLink like this
headers={headers}
data={newBillingReport()}
headers should call the function const headers = formatUserName()
I have this two collection in Firestore which I would like to display in one Flatlist in React Native.
First collection looks like this: (this is created by the user)
collection_1 : [
{
id: a1b2c3,
name: 'joe'
}
{
id2: d4e5f6,
name: 'jane'
}
]
and second collection looks like this: (this is created by the friend user)
collection_2: [
{
id: z9y8x7
userId: 'a1b2c3',
seenCount: 10,
},
{
id: w7v6u5
userId: 'd4e5f6'
seenCount: 5,
},
]
and I would like to display the list with the name and the seen count next to each other with the condition of the collection_1 id is the same with the collection_2 userId:
joe (10)
jane (5)
but currently my I only have collection_1 in my flatlist:
<FlatList
data={collection_1}
keyExtractor={(item, index) => String(index)}
renderItem={({ item }) => (
<View>
<Text>
{item.name}
</Text>
</View>
)}
/>
is it possible with Flatlist? or is there a better way to this?
You should create a new array from 2 collection lists. data is what you need in the below example.
const collection1 = [
{
id: 'a1b2c3',
name: 'joe'
},
{
id: 'd4e5f6',
name: 'jane'
}
];
const collection2 = [
{
id: 'z9y8x7',
userId: 'a1b2c3',
seenCount: 10
},
{
id: 'w7v6u5',
userId: 'd4e5f6',
seenCount: 5
},
{
id: 'o1j3o2',
userId: 'd4e5f6',
seenCount: 7
}
];
const seenCounts = {};
collection2.forEach((item) => {
if (seenCounts[item.userId]) {
seenCounts[item.userId] += item.seenCount;
} else {
seenCounts[item.userId] = item.seenCount;
}
});
const data = collection1.map((item) => ({
...item,
seenCount: seenCounts[item.id]
}));
console.log(data);
You need to combine the arrays so that your object consist of both name and screen (Please run snippet).
Then u can do it like this.
<FlatList
data={final} //this is the combined array
keyExtractor={(item, index) => String(index)}
renderItem={({ item }) => (
<View>
<Text>
{item.name} ({item.seenCount})
</Text>
</View>
)}
/>
Combine Array
let collection_1 = [{
id: 'a1b2c3',
name: 'joe'
},
{
id: 'd4e5f6',
name: 'jane'
}
]
let collection_2 = [{
id: 'z9y8x7',
userId: 'a1b2c3',
seenCount: 10,
},
{
id: 'w7v6u5',
userId: 'd4e5f6',
seenCount: 5,
}
]
let final = collection_2.map(user => {
let name = collection_1.filter((users) => users.id === user.userId)[0].name
return { ...user,
name
}
})
console.log('final', final)
Here is my entire code at Home.js file
export default function Home({navigation}) {
const [reviews, setReviews] = useState([
{ title: 'Alfa Romeo 147 1.9JTD', rating: 2020, body: 340000, sg: ['ABS ', 'CD ', 'ESP '], key: '1' },
{ title: 'Gotta Catch Them All (again)', body: 'lorem ipsum', key: '2' },
{ title: 'Not So "Final" Fantasy', body:'lorem ipsum', key: '3' },
{ title: 'Alfaromeo', rating: 3200, body: 'blablabla', first:'loremlo', key: '4' },
{ title: 'Gotta Catch Them All (again)', rating: 4, body: 'lorem ipsum', key: '5' },
{ title: 'Not So "Final" Fantasy', rating: 3, body: 'lorem ipsum', key: '6' },
{ title: 'Alfaromeo', rating: 3200, body: 'sadaa', key: '7' },
{ title: 'Gotta Catch Them All (again)', rating: 4, body: 'lorem ipsum', key: '8' },
]);
return (
<View style={styles.container}>
<FlatList data={reviews} renderItem={({ item }) => (
<TouchableOpacity onPress={() => navigation.navigate('ReviewDetails', item)}>
<View style={styles.content}>
<Image
style={styles.image}
source={{
uri: 'https://www.autoplac-cg.me/storage/1871/conversions/5f9eb91821de1_20FB3486-4A0A-4B4A-B13C-CAE912950E22-thumb.jpg',
}}
/>
<Text style={styles.headertext}>{item.title }</Text>
<Text style={styles.infotext}>{item.rating}god. | {item.body}km <Text style={styles.collapse}>+</Text></Text>
</View>
</TouchableOpacity>
)} />
</View>
);
}
So I want to put first on FlatList that review who has in array 'first', so in code its fourth. How I can do that?
I want to this be first on FlatList
{ title: 'Alfaromeo', rating: 3200, body: 'blablabla', first:'loremlo', key: '4' }
I believe that the best way to do that is to sort the data as needed and then render it with FlatList.
The sort logic may be the way you need, which means that you are free to "order by 'anything'" if you wish.
According to the data set and information you provided, the business rule, as I understood, is to show the items with fisrt flag at first place. So, the sorting could be like this:
export default function Home({navigation}) {
const [reviews, setReviews] = useState([
{ title: 'Alfa Romeo 147 1.9JTD', rating: 2020, body: 340000, sg: ['ABS ', 'CD ', 'ESP '], key: '1' },
{ title: 'Gotta Catch Them All (again)', body: 'lorem ipsum', key: '2' },
{ title: 'Not So "Final" Fantasy', body:'lorem ipsum', key: '3' },
{ title: 'Alfaromeo', rating: 3200, body: 'blablabla', first:'loremlo', key: '4' },
{ title: 'Gotta Catch Them All (again)', rating: 4, body: 'lorem ipsum', key: '5' },
{ title: 'Not So "Final" Fantasy', rating: 3, body: 'lorem ipsum', key: '6' },
{ title: 'Alfaromeo', rating: 3200, body: 'sadaa', key: '7' },
{ title: 'Gotta Catch Them All (again)', rating: 4, body: 'lorem ipsum', key: '8' },
]);
function renderItem(item) {
return (
<TouchableOpacity onPress={() => navigation.navigate('ReviewDetails', item)}>
<View style={styles.content}>
<Image
style={styles.image}
source={{
uri: 'https://www.autoplac-cg.me/storage/1871/conversions/5f9eb91821de1_20FB3486-4A0A-4B4A-B13C-CAE912950E22-thumb.jpg',
}}
/>
<Text style={styles.headertext}>{item.title}</Text>
<Text style={styles.infotext}>{item.rating}god. | {item.body}km <Text style={styles.collapse}>+</Text></Text>
</View>
</TouchableOpacity>
);
}
function sortData() {
let sortedArray = [];
// If the item contains "first" property, it will be placed at the beginning of the sortedArray, else it will be at the end of it
reviews.forEach(review => (
review.first
? sortedArray = [review, ...sortedArray]
: sortedArray.push(review)
));
return sortedArray;
}
return (
<View style={styles.container}>
<FlatList
data={sortData()}
renderItem={({ item }) => renderItem(item)}
/>
</View>
);
}
I moved the code to render an item to a separated function just for convenience
Use array.sort with providing compare function
See: https://www.w3schools.com/js/js_array_sort.asp
You can use
reviews.sort((a, b) => a.rating - b.rating)
This will sort your reviews array.
You can do is
var reviews1 = reviews.filter(function (el) {
return el["first"];
});
var reviews2 = reviews.filter(function (el) {
return el["first"] === undefined;
});
const reviewsFinalArray = reviews1.concat(reviews2); // Your result
I'm trying to get the structure below.
https://imgur.com/NBRGlhM
Three columns, with titles, and column cards that can be moved within only one column. this.state.item ['lists'] moving to the component SortableList. Then iterates after items.map ((item, index) => and moves to the component SortableItem. Then iterates aftervalue.listItems and wants to display the title of columns and cards in the column. I get the error:
Cannot read property ' indexOf 'of undefined
Demo here: https://stackblitz.com/edit/react-jpszoq
import {SortableContainer, SortableElement} from 'react-sortable-hoc';
import arrayMove from 'array-move';
const SortableItem = SortableElement(({value}) => {
return(
value.listItems.map((it, index) => {
<li>{it.title}</li>
})
)
})
const SortableList = SortableContainer(({items}) => {
console.log(items)
return (
<ul>
{
items.map((item, index) =>
<div>{item.name}
<SortableItem key={`item-${item.id}`} index={index} value={item} />
</div>
)
}
</ul>
);
});
class App extends Component {
constructor() {
super();
this.state = {
item: {
id: "abc123",
name: "AAA",
lists: [
{
id: "def456",
list_id: "654wer",
title: 'List1',
desc: "description",
listItems: [
{
id: "ghj678",
title: "ListItems1",
listItemsId: "88abf1"
},
{
id: "poi098",
title: "ListItems2",
listItemsId: "2a49f25"
}
]
},
{
id: "1ef456",
list_id: "654wer",
title: 'List 2',
desc: "description",
listItems: [
{
id: "1hj678",
title: "ListItems3",
listItemsId: "18abf1"
},
{
id: "1oi098",
title: "ListItems4",
listItemsId: "1a49f25"
}
]
},
{
id: "2ef456",
title: 'List 3',
list_id: "254wer",
desc: "description",
listItems: [
{
id: "2hj678",
title: "ListItems5",
listItemsId: "28abf1"
},
{
id: "2oi098",
title: "ListItems6",
listItemsId: "234a49f25"
}
]
}
]
}
};
}
onSortEnd = ({oldIndex, newIndex}) => {
this.setState(({lists}) => ({
lists: arrayMove(lists, oldIndex, newIndex),
}));
};
render() {
return <SortableList items={this.state.item['lists']} onSortEnd={this.onSortEnd} />;
}
}
you're missing a return on L14 in the function passed to listItems.map. Alternatively, you could remove the braces.