React Native onPress button in FlatList - javascript

Maybe it is a noob problem but I can not make an onPress for FlatList in react native made of an items of array in the separate file popularData and categoriesData. In popularData I have an array with information of it is selected or not boolean type, I just want to get this info of the array, and put it to onPress method to change the selected to true or false if its pressed and change it backgroundColor to another, and further to fetch that data and get it to some variable. Can you please help me ? I've tried with handleClick, navigation but still can't reach that what I want These are the codes:
File 1 with the info in array:
const popularData = [
{
id: '1',
image: require('../images/pizza1.png'),
title: 'Salami Pizza',
weight: '600 gr',
rating: '5.0',
cena: '26,99 zł',
rozmiarNazwa: 'Średnia',
rozmiarNumer: '42 cm',
ciasto: 'Cienkie',
czasDostawy: '~30 min',
skladniki: [
{
id: '0',
name: 'Sos pomidorowy',
image: require('../images/skladniki/sosczerwony.png'),
selected: true,
},
{
id: '1',
name: 'Ser',
image: require('../images/skladniki/ser.png'),
selected: true,
},
{
id: '2',
name: 'Salami',
image: require('../images/skladniki/salamenapoli.png'),
selected: true,
},
{
id: '3',
name: 'Oliwki zielone',
image: require('../images/skladniki/zieloneoliwki.png'),
selected: true,
},
{
id: '5',
name: 'Pieczarki',
image: require('../images/skladniki/pieczarki.png'),
selected: false,
},
{
id: '6',
name: 'Tabasco Habanero',
image: require('../images/skladniki/tabascobordowe.png'),
selected: false,
},
],
},
export default popularData;
ANOTHER FILE the app screen
const renderSkladnikiItem = ({ item }) => {
return (
<View style={[styles.skladnikiItemWrapper, {
backgroundColor: item.selected ? colors.glowny : colors.tlo,
marginLeft: item.id === '0' ? 20 : 5,
} ]}>
<Image source={item.image} style={styles.skladnikImage} />
</View>
);
};
{/* Skladniki */}
<View style={styles.skladnikiWrapper}>
<Text style={styles.skladnikiTytul}>Składniki</Text>
<View style={styles.sklanikiListaWrapper}>
<FlatList
data={item.skladniki}
renderItem={renderSkladnikiItem}
keyExtractor={(item) => item.id}
horizontal={true}
showsHorizontalScrollIndicator={false}
decelerationRate="normal"
/>
</View>
</View>

You can enclose your View in TouchableOpacity or TouchbaleHighlight to apply onPress on.
const renderSkladnikiItem = ({ item }) => {
return (
<TouchableHighlight
key={item.key}
onPress={() => onPress(item)}
>
<View style={[styles.skladnikiItemWrapper, {
backgroundColor: item.selected ? colors.glowny : colors.tlo,
marginLeft: item.id === '0' ? 20 : 5,
} ]}>
<Image source={item.image} style={styles.skladnikImage} />
</View>
</TouchableHighlight>
);
};

Related

react native (type script) : Selecting the father sons check box in flatlist

My goal is that when user select a checkbox of one of the fathers ('Non Veg Biryanis','Pizzas','Drinks','Deserts') in the flatlist, then those sons who belong to him are also selected.
When the check box is removed from all the children belonging to the same father, then the check box is also removed for the father as well.
this is my example (but its not works as can see, the father 'Pizzas' not checked but his sons checked)
The data is:
[
{
title: 'Non Veg Biryanis',
checked: false,
data: [
{ key: 'chicken', value: false, checked: false },
{ key: 'meat', value: false, checked: false },
{ key: 'veg', value: false, checked: false },
],
},
{
title: 'Pizzas',
checked: false,
data: [
{ key: 'olives', value: false, checked: false },
{ key: 'green cheese', value: false, checked: false },
{ key: 'paprika', value: false, checked: false },
],
},
{
title: 'Drinks',
checked: false,
data: [
{ key: 'cola', value: false, checked: false },
{ key: 'sprite', value: false, checked: false },
{ key: 'orange', value: false, checked: false },
],
},
{
title: 'Deserts',
checked: false,
data: [
{ key: 'cake', value: false, checked: false },
{ key: 'ice-cream', value: false, checked: false },
{ key: 'pie', value: false, checked: false },
],
},
]
The Accordian:
import React, { useState } from 'react';
import { View, TouchableOpacity, Text, FlatList, LayoutAnimation, Platform, UIManager } from 'react-native';
import Icon from 'react-native-vector-icons/MaterialIcons';
import styles from './AccordianStyle';
const Accordian = props => {
const [data, setData] = useState(props.item);
const [expanded, setExpanded] = useState(false);
if (Platform.OS === 'android') {
UIManager.setLayoutAnimationEnabledExperimental(true);
}
const onClick = (index: number) => {
console.log(data);
const temp = { ...data };
temp.data[index].checked = !temp.data[index].checked;
setData(temp);
};
const onClickFather = () => {
const temp = { ...data };
temp.checked = !temp.checked;
if (temp.checked) {
temp.data = temp.data.map((item: { checked: boolean }) => {
item.checked = true;
});
}
console.log(temp);
setData(temp);
};
const toggleExpand = () => {
LayoutAnimation.configureNext(LayoutAnimation.Presets.easeInEaseOut);
setExpanded(!expanded);
};
return (
<View>
<View style={styles.row}>
<TouchableOpacity onPress={() => onClickFather()}>
<Icon name={'check-circle'} size={24} color={data.checked ? 'green' : '#d3d7de'} />
</TouchableOpacity>
<Text style={[styles.title]}>{data.title}</Text>
<TouchableOpacity style={styles.row} onPress={() => toggleExpand()}>
<Icon name={expanded ? 'keyboard-arrow-up' : 'keyboard-arrow-down'} size={30} color={'grey'} />
</TouchableOpacity>
</View>
<View style={styles.parentHr} />
{expanded && (
<View style={{}}>
<FlatList
data={data.data}
numColumns={1}
scrollEnabled={false}
renderItem={({ item, index }) => (
<View>
<TouchableOpacity
style={[styles.childRow, styles.button, item.checked ? styles.btnActive : styles.btnInActive]}
onPress={() => onClick(index)}
>
<Text style={[styles.itemInActive]}>{item.key}</Text>
<Icon name={'check-circle'} size={24} color={item.checked ? 'green' : '#d3d7de'} />
</TouchableOpacity>
<View style={styles.childHr} />
</View>
)}
/>
</View>
)}
</View>
);
};
export default Accordian;
Modify your onClick function so that it loops through each entry in the data array with the some() method and check if any of the children (sons) are clicked.
If one of them is, then update set the checked property accordingly.
const onClick = (index: number) => {
setData(prevState => {
const newData = prevState.data.map((entry, entryIndex) =>
index === entryIndex ?
({ ...entry, checked: !entry.checked }) :
entry
);
const isAnyChildChecked = newData.some(({ checked }) =>
checked === true
);
return ({
...prevState,
checked: isAnyChildChecked,
data: newData
})
});
};
If the parent is clicked, then check or uncheck all the children as well.
const onClickFather = () => {
setData(prevState => {
const isParentChecked = !prevState.checked;
return ({
...prevState,
checked: isParentChecked,
data: prevState.data.map(item => ({ ...item, checked: isParentChecked }))
});
});
};

Material UI TreeView Filter

I have a json of files and folders to be rendered as a MUI TreeView component. The treeview works perfectly fine. I need to add a filter to this Treeview component. I am new to React and Javascript. Could you please help me out with this?
The JSON structure for the treeview looks something like this:
const treeViewApiData = {
name: "fl1",
path: "folder 1",
children: [
{
name: "f1",
path: "file 1",
children: [],
isFile: true,
},
{
name: "f2",
path: "file 2",
children: [],
isFile: true,
},
{
name: "f3",
path: "file 3",
children: [],
isFile: true,
},
],
isFile: false,
};
The code for my richObjectTreeView.js looks like this:
export default function RichObjectTreeView(props) {
const dispatch = useDispatch();
const handleOnItemClick = (event, nodeIds) => {
// Displays the node clicked onto the dashboard if it is a file.
}
};
const renderTree = (nodes) => {
if (!nodes || nodes.length === 0) {
return null;
}
return (
<TreeItem key={nodes.path} nodeId={nodes.path} label={nodes.name}>
{Array.isArray(nodes.children)
? nodes.children.map((node) => renderTree(node))
: null}
</TreeItem>
);
};
return props.treeViewApiData ? (
<TreeView
aria-label="rich object"
defaultCollapseIcon={
<>
<ExpandMoreIcon />
<FolderOpenIcon sx={{ marginRight: "12px" }} />
</>
}
defaultExpanded={["root"]}
defaultExpandIcon={
<>
<ChevronRightIcon />
<FolderIcon sx={{ marginRight: "12px" }} />
</>
}
defaultEndIcon={<ArticleIcon />}
sx={{ height: 110, flexGrow: 1, maxWidth: 400, overflowY: "auto" }}
onNodeFocus={handleOnItemClick}
>
{renderTree(props.treeViewApiData)}
</TreeView>
) : (
<CircularProgress sx={{ marginLeft: "100px", marginTop: "100px" }} />
);
}

Can't Scroll SectionList Until Last Item Because Out of Viewport

I have issue with a long sectionlist that went off outside of viewport, how can I make it work normally? I have tried using flexShrink:1 but it didn't work. other solution I have tried is using <ScrollView> but it giving me error VirtualizedLists should never be nested inside plain ScrollViews with the same orientation because it can break windowing and other functionality - use another VirtualizedList-backed container instead. I really need help for this issue, how to make the SectionList stay inside and work normally? here is some example code I have tried
App.js
import React, { Component } from "react";
import { View, Text } from "react-native";
import AnotherJS from "./AnotherJS";
export default class App extends Component {
render(){
return(
<View style={{backgroundColor:"#5273B7", flex:1, padding:10}}>
<View style={{backgroundColor:"#ff0000", borderTopLeftRadius:10, borderTopRightRadius:10, padding: 10}}>
<View style={{backgroundColor:"#ffffff", borderRadius:10}}>
<Text>Picker Here</Text>
</View>
</View>
<View style={{backgroundColor:"#00ff00", flex:1, borderBottomLeftRadius:10, borderBottomRightRadius:10, padding:10}}>
<View style={{flexShrink:1}}>
<AnotherJS/>
</View>
</View>
</View>
)
}
}
AnotherJS.js
import React, { Component } from "react";
import {View, Text, SectionList } from "react-native";
import Accordion from "react-native-collapsible/Accordion";
const dataArray = [
{ title: "Info 1",
content: [
{ title: "1", data: ["1"]},
{ title: "2", data: ["2"]},
{ title: "1", data: ["1"]},
{ title: "2", data: ["2"]},
{ title: "1", data: ["1"]},
{ title: "2", data: ["2"]},
{ title: "1", data: ["1"]},
{ title: "2", data: ["2"]},
{ title: "1", data: ["1"]},
{ title: "2", data: ["2"]},
{ title: "1", data: ["1"]},
{ title: "2", data: ["2"]},
{ title: "1", data: ["1"]},
{ title: "2", data: ["2"]},
] },
{ title: "Info 2",
content: [
{ title: "1", data: ["1"]},
{ title: "2", data: ["2"]},
] },
{ title: "info 3",
content: [
{ title: "1", data: ["1"]},
{ title: "2", data: ["2"]},
] }
];
const Item = ({ title }) => (
<View>
<Text>{title}</Text>
</View>
);
export default class AnotherJS extends Component {
state = {
activeSections: [0],
};
_renderHeader = (section) => {
return(
<View style={{
flexDirection: "row",
padding: 10,
justifyContent: "space-between",
alignItems: "center" ,
backgroundColor: "#A9DAD6" }}>
<Text style={{ fontWeight: "600" }}>{section.title}</Text>
</View>
);
};
_renderContent = (section) => {
return(
<SectionList
sections={section.content}
keyExtractor={(item, index) => item + index}
renderItem={({ item }) => <Item title={item} />}
renderSectionHeader={({ section: { title } }) => (
<Text>{title}</Text>
)}
/>
);
};
_updateSections = (activeSections) => {
this.setState({ activeSections });
};
render() {
return (
<View style={{flexShrink:1}}>
<Accordion
sections = {dataArray}
activeSections = {this.state.activeSections}
renderHeader = {this._renderHeader}
renderContent = {this._renderContent}
onChange = {this._updateSections}
/>
</View>
);
}
}
Try to wrap your list component in a container.
_renderContent = (section) => {
return(
<ListContainer style={{flex: 1}}> // you can add borders for clarity
<SectionList
sections={section.content}
keyExtractor={(item, index) => item + index}
renderItem={({ item }) => <Item title={item} />}
renderSectionHeader={({ section: { title } }) => (
<Text>{title}</Text>
<ListContainer/>
)}
/>
);
};
ListContainer code:
const ListContainer = styled.View`
flex: 1;
`;

Loop through object, if condition is met, return additional mark up

I am looping through an object, it loops through the DropZone Object 4 times,
each DropZone has an id
dropZones: {
'zone-1': {
id: 'zone-1',
title: 'zone-1',
info: 'Strongest',
items: [
{ id: 1, content: 'I am label 1' },
{ id: 2, content: 'I am label 2' },
],
maxItems: 3,
color: '#8bea97',
},
'zone-2': {
id: 'zone-2',
title: 'zone-2',
info: 'Strong',
items: [],
maxItems: 5,
color: '#bef7c6',
},
'zone-3': {
id: 'zone-3',
title: 'zone-3',
info: 'Weakest',
items: [{ id: 4, content: 'I am label 4' }],
color: '#e6ffe9',
},
'start-zone': {
id: 'start-zone',
title: 'Inactive',
info: 'Inactive',
items: [
{ id: 5, content: 'I am label 5' },
{ id: 6, content: 'I am label 6' },
{ id: 7, content: 'I am label 7' },
{ id: 8, content: 'I am label 8' },
{ id: 9, content: 'I am label 9' },
],
color: 'white',
},
},
For the 4th Dropzone, which has an id of start-zone, I want to render some JSX markup.
I check if the dropzone has the zoneId of start-zone, and then loop through it to render my markup.
if (zoneId === 'start-zone') {
dropZone.items.map(item => (
<Placeholder>
<Title>Labels</Title>
<PlaceholderButton type="button" onClick={() => setShowInput(!showInput)}>
+
</PlaceholderButton>
<Item
draggable
onDrag={() => {
setDragSource(dropZone.id)
setDraggedItem(item)
}}
key={item.id}
>
{item.content}
</Item>
</Placeholder>
))
}
My Item's are rendering just fine. However <Title />, PlaceholderButton is not being rendered in my UI.
Heres the full loop
<div onDrop={onDrop}>
{Object.keys(currentAnswer).length !== 0
? zoneOrder.map(zoneId => {
const dropZone = currentAnswer[zoneId]
if (zoneId === 'start-zone') {
dropZone.items.map(item => (
<Placeholder>
<Title>Labels</Title>
<PlaceholderButton type="button" onClick={() => setShowInput(!showInput)}>
+
</PlaceholderButton>
<Item
draggable
onDrag={() => {
setDragSource(dropZone.id)
setDraggedItem(item)
}}
key={item.id}
>
{item.content}
</Item>
</Placeholder>
))
}
return (
<DropZone
onDragOver={event => onDragOver(event)}
data-droppable
id={dropZone.id}
key={dropZone.id}
onDragLeave={onDragLeave}
>
{dropZone.items.map(item => (
<Item
draggable
onDrag={() => {
setDragSource(dropZone.id)
setDraggedItem(item)
}}
key={item.id}
>
{item.content}
<DropZoneLabel>{dropZone.title}</DropZoneLabel>
</Item>
))}
</DropZone>
)
})
: null}
There's no errors in my console to indicate why this would happen. Is there a better way to render specfic mark up for my DropZone?
I think the problem is that you're not saving the results of the .map() and passing it to render.
Inside your if, you do
if (zoneId === 'start-zone') {
dropZone.items.map(item => (
...
)
but you don't assign it to a variable, so the resulting JSX is thrown away.
try
let result;
if (zoneId === 'start-zone') {
result = dropZone.items.map(item => (
...
)
}
return (
<DropZone
onDragOver={event => onDragOver(event)}
data-droppable
id={dropZone.id}
key={dropZone.id}
onDragLeave={onDragLeave}
>
{result}
</DropZone>
)

How to identify a certain object id of an array to change the state of an element in the same object?

I am trying to identify a specific panel in an array when it is expanded and be able to connect that panel's id to the button, as well as disable the button if no panel is expanded or more than 1 panel is expanded. For whatever reason, it's not taking in the id at all. Also, I am having problems with how to disable the button correctly.
export default class WorkoutList extends Component {
constructor(props) {
super(props);
this.state = {
workoutlist: [
{
id: uuid.v4(),
name: 'Leg Day',
date: '08/09/19',
duration: 60,
exerciselist: [
{
id: uuid.v4(),
exerciseName: 'Squats',
numberOfSets: 3,
reps: 12,
weight: 135,
},
{
id: uuid.v4(),
exerciseName: 'Leg press',
numberOfSets: 3,
reps: 10,
weight: 150,
},
{
id: uuid.v4(),
exerciseName: 'Lunges',
numberOfSets: 4,
reps: 12,
},
],
selected: false,
},
{
id: uuid.v4(),
name: 'Arm Day',
date: '08/10/19',
duration: 90,
exerciselist: [
{
id: uuid.v4(),
exerciseName: 'Bench Press',
numberOfSets: 5,
reps: 5,
weight: 225,
},
{
id: uuid.v4(),
exerciseName: 'Chest Flies',
numberOfSets: 3,
reps: 10,
weight: 50,
},
{
id: uuid.v4(),
exerciseName: 'Tricep Extensions',
numberOfSets: 4,
reps: 12,
weight: 70,
},
],
selected: false,
},
{
id: uuid.v4(),
name: 'Running',
date: '08/11/19',
duration: 40,
exerciselist: [],
selected: false,
},
],
disabled: false
}
this.handleSelectedPanel = this.handleSelectedPanel.bind(this);
this.handleButton = this.handleButton.bind(this);
}
handleSelectedPanel(id) {
const { workoutlist } = this.state;
this.setState({
workoutlist: workoutlist.map(workout => {
if (workout.id === id) {
workout.selected = !workout.selected
}
return workout;
})
})
}
handleButton(){
const { workoutlist, disabled } = this.state;
let count = 0;
workoutlist.map((workout) => {
if(workout.selected === true) {
count = count + 1;
}
return count;
})
if (count !== 1) {
this.setState({
disabled: true
})
} else {
this.setState({
disabled: false
})
}
return disabled;
}
render() {
const { workoutlist } = this.state;
return (
<div>
<CssBaseline />
<ClientMenuBar title="My Workouts" />
<div style={styles.workoutlist}>
<Paper style={styles.paper} elevation={0}>
{workoutlist.map((workout) => (
<WorkoutItem
key={workout.id}
workout={workout}
onSelectedPanel={this.handleSelectedPanel}
/>
))}
</Paper>
<Button
variant="contained"
color="primary"
size="small"
style={styles.button}
disabled={this.handleButton}
>
Start Workout
</Button>
</div>
</div>
)
}
}
export default class WorkoutItem extends Component {
constructor(props){
super(props);
this.handleSelectedPanel = this.handleSelectedPanel.bind(this);
}
handleSelectedPanel(e) {
this.props.onSelectedPanel(e.target.id);
}
render() {
const { id, name, date, duration, exerciselist } = this.props.workout;
return (
<ExpansionPanel style={styles.panel} id={id} onChange={this.handleSelectedPanel}>
<ExpansionPanelSummary>
<Typography variant="button" style={{ width: "33%" }}>
{name}
</Typography>
<Typography variant="button" style={{ width: "33%" }}>
({date})
</Typography>
<Typography align="right" style={{ width: "33%" }}>
~{duration} mins
</Typography>
</ExpansionPanelSummary>
<ExpansionPanelDetails>
<Table size="medium" style={styles.table}>
<TableHead>
<TableRow>
<TableCell padding="none">Name</TableCell>
<TableCell padding="none" align="right"># of sets</TableCell>
<TableCell padding="none" align="right">reps</TableCell>
<TableCell padding="none" align="right">weight</TableCell>
</TableRow>
</TableHead>
<TableBody>
{exerciselist.map((exercise) => (
<ExerciseList
key={exercise.id}
exercise={exercise}
/>
))}
</TableBody>
</Table>
<ExpansionPanelActions disableSpacing style={styles.actionButton}>
<Button color="primary" size="small" disableRipple>
edit
</Button>
</ExpansionPanelActions>
</ExpansionPanelDetails>
</ExpansionPanel>
)
}
}
It doesn't seem to be taking in the id at all, and when i try to disable the button, it throws this error:
Warning: Failed prop type: Invalid prop disabled of type function supplied to ForwardRef(Button), expected boolean.
The warning you are seeing comes from:
<Button
variant="contained"
color="primary"
size="small"
style={styles.button}
disabled={this.handleButton}
>
In the error it says a function is passed to disabled which should be a boolean, so change the prop that disabled takes to be that boolean (rather than the function this.handleButton).
e.target.id doesn't have what you actually want in there (it actually probably isn't a thing). You can use e.target.value to get a value out of something like an input where you want to get something information from the DOM node you are working with but in this case the information isn't something entered and actually something that the component already has in its scope (in the props). So instead of:
handleSelectedPanel(e) {
this.props.onSelectedPanel(e.target.id);
}
do this
handleSelectedPanel(e) {
this.props.onSelectedPanel(this.props.workout.id);
}

Categories