Flatlist doesn't re-render my items in multiselect - javascript

i'm quite new to react-native, lately i'm struggling with multiselect and highlights my items in a flatlist, could anyone help me out please! :D
export default function opzioni( {navigation} ){
const renderItem = ({item}) => (
<TouchableOpacity style={item.stile} onPress={() => selectItem(item)}>
<Text>{item.titolo}</Text>
</TouchableOpacity>
)
const selectItem = (item) => {
item.isSelect = !item.isSelect;
item.stile = item.isSelect ? styles.itemSelected : styles.item;
}
return(
<SafeAreaView style={styles.containerUtente}>
<ScrollView>
<Text>SELEZIONA COSA ELIMINARE:</Text>
<FlatList
numColumns={4}
data={data}
renderItem={renderItem}
keyExtractor={item => item.id.toString()}
extraData={data}
/>
</ScrollView>
</SafeAreaView>
)
}
and the data is like so, i have an object with title to show in the flatlist, an id to give in the flatlist, isSelect for selecting or deselecting the item and lastly a stile for re-render the item in a different color background
{
titolo: "no alcol",
id: 1,
isSelect: false,
stile: styles.item
}

Create a state and store the data in that...then use it in FlatList..and your select function were also wrong
Working Example
Write it like this
export default function opzioni({ navigation }) {
const [Data, SetData] = React.useState(data);
const renderItem = ({ item }) => (
<TouchableOpacity
onPress={() => selectItem(item.id)}
style={item.isSelect ? styles.itemSelected : styles.item}>
<Text>{item.titolo}</Text>
</TouchableOpacity>
);
const selectItem = (id) => {
let temp = [...Data];
for (let i = 0; i < temp.length; i++) {
if (temp[i].id === id) {
temp[i].isSelect = temp[i].isSelect ? false : true;
break;
}
}
SetData(temp);
};
return (
<SafeAreaView style={styles.containerUtente}>
<ScrollView>
<Text>SELEZIONA COSA ELIMINARE:</Text>
<FlatList
numColumns={4}
data={Data}
renderItem={renderItem}
keyExtractor={(item) => item.id.toString()}
/>
</ScrollView>
</SafeAreaView>
);
}

Related

React native: Update value of object in array in state

I have a component which changes the state when checkbox is checked and the data needs to be updated of the object in the array.
The component state looks something like this
{
key:1,
todo:"Something",
isChecked:false
}
i have 3 files:
AddTodo.js Which passes state & setState to an component TodoList which passes it the subcomponent TodoItem.
I am unable to update the state from TodoItem , I need to implement a function that finds the object from array and updates its isChecked state.
AddTodo.js
function AddTodo() {
const [state, setState] = useState(false);
const [todos, addTodos] = useState([]);
var keys = (todos || []).length;
return (
<View style={styles.container}>
<Modal
animationType="slide"
transparent={true}
visible={state}
statusBarTranslucent={true}
>
<View style={styles.itemsContainer}>
<GetInfoDialog
state={state}
stateChange={setState}
addItem={addTodos}
numKeys={keys}
/>
</View>
</Modal>
{(todos || []).length > 0 ? (
<TodoList data={todos} updateState={addTodos} />
) : null}
<TouchableOpacity
style={styles.btn}
onPress={() => {
setState(true);
}}
>
<Text style={styles.text}>Add New</Text>
</TouchableOpacity>
</View>
);
}
TodoList.js
function TodoList(props) {
return (
<View style={styles.todoList}>
<FlatList
data={props.data}
renderItem={({ item }) => {
console.log(item);
return (
<TodoItem
list={props.data}
itemKey={item.key}
todo={item.todo}
isChecked={item.isChecked}
updateState={props.updateState}
/>
);
}}
backgroundColor={"#000000"}
alignItems={"center"}
justifyContent={"space-between"}
/>
</View>
);
}
TodoItem.js
function TodoItem(props) {
const [checked, setCheck] = useState(props.isChecked);
return (
<View style={styles.todoItem}>
<Checkbox
value={checked}
onValueChange={() => {
setCheck(!checked);
}}
style={styles.checkbox}
/>
<Text style={styles.text}>{props.todo}</Text>
</View>
);
}
renderItem={({ item, index }) => {
console.log(item);
return (
<TodoItem
list={props.data}
itemKey={item.key}
todo={item.todo}
isChecked={item.isChecked}
updateState={props.updateState}
setChecked={(value)=>{
let updatedList = [...yourTodosList]
updatedlist[index].isChecked=value
setTodos(updatedList)
}}
/>
);
}}
and in your todo item
onValueChange={(value) => {
props.setChecked(value);
}}
i also don't think that you need an is checked state in your todo component since you are passing that through props (so delete const [checked, setCheck] = useState(props.isChecked) line and just use the value you are getting from props.isChecked)
didn't pay much attention to your variable names but this should put you on the right track
as per React Native Hooks you have to call
useEffect(() => {
setCheck(checked);
}, [checked]) // now this listens to changes in contact
in TodoItem.tsx

Dynamically render component for each item in array React Native

Beginner question here, not sure exactly what this would be considered, but I'm trying to make a form where a user can add and remove input rows upon pressing a button. What do I need to change to render new components or remove components when the Add or Remove buttons are pressed? Right now, the Add and Remove button change the textInput array appropriately, but components are not actually being added or removed.
Here is my current code:
FormScreen.js
import React from 'react';
import { View, StyleSheet, ScrollView } from 'react-native';
import { Button, Caption } from 'react-native-paper';
import InputCard from '../components/InputCard';
const FormScreen = props => {
const textInput = [1,2,3];
const addTextInput = () => {
let currArr = textInput;
let lastNum = currArr[currArr.length -1]
let nextNum = lastNum + 1
console.log(currArr, lastNum, nextNum);
textInput.push(
nextNum
);
console.log(textInput);
};
const removeTextInput = () => {
textInput.pop();
console.log(textInput);
};
return (
<ScrollView>
<View style={styles.col}>
<View style={styles.row}>
<Caption>Favorite colors?</Caption>
</View>
<View style={styles.row}>
<View>
{textInput.map(key => {
return (
<InputCard key={key}/>
);
})}
</View>
</View>
<View>
<View style={styles.col}>
<Button title='Add' onPress={() => addTextInput()}>Add</Button>
</View>
<View style={styles.col}>
<Button title='Remove' onPress={() => removeTextInput()}>Remove</Button>
</View>
</View>
</View>
</ScrollView>
);
};
export default FormScreen;
InputCard.js
import React, { useState } from "react";
import { View, StyleSheet } from 'react-native';
import { Caption, Card, TextInput } from "react-native-paper";
const InputCard = (props) => {
const [input, setInput] = useState('');
return (
<View>
<Card>
<Card.Content>
<Caption>Item {props.key}</Caption>
<View style={styles.row}>
<View style={styles.half}>
<TextInput
label="Input"
value={input}
onChangeText={input => setInput(input)}
mode="outlined"
style={styles.textfield}
/>
</View>
</View>
</Card.Content>
</Card>
</View>
);
}
export default InputCard;
Instead of storing it in a array, try to do something like this, using 2 states.
const [totalTextInput, setTotalTextInput] = useState([])//initial state, set it to any data you want.
const [count, setCount] = useState(0);
const addTextInput = () => {
setCount((prevState) => prevState + 1);
setTotalTextInput((prevState) => {
const newTextInput = Array.from(prevState); // CREATING A NEW ARRAY OBJECT
newTextInput.push(count);
return newTextInput;
});
};
const removeTextInput = () => {
setTotalTextInput((prevState) => {
const newTextInput = Array.from(prevState); // CREATING A NEW ARRAY OBJECT
newTextInput.pop();
return newTextInput;
});
};
And in your code:
<View>
{totalTextInput.map(key => {
return (
<InputCard key={key}/>
);
})}
</View>

Changing style of specific component returned from map function onClick

I am trying to change the style of individual TouchableOpacity components that have been returned from a map function.
Here is the component:
Example = ({ props }) => {
return (
<View>
{props.listExample.map(({ id }) => {
return (
<React.Fragment key={id}>
<TouchableOpacity
style={styles.button}
onPress={() => console.log(id)}>
<Image source={require('example.jpg')} />
</TouchableOpacity>
</React.Fragment>
);
})}
</View>
);
};
Let TouchableOpacity = TO.
The map function returns about 30 TOs with unique IDs. When I click the TOs, I can see their unique ID in the console log. I want to know how I can modify the style of an individual TO.
Here is my render function which uses the functional component Example.
render() {
return (
<View style={styles.body}>
<ScrollView>
<View style={styles.column}>
<this.Example props={{ listExample: this.getList() }} />
</View>
</ScrollView>
</View>
);
}
What I have tried:
referencing this stackoverflow post, I tried to create a function which changed the style of the TO when it is clicked. But the result of this changed all the TOs in the UI since of the way it is mapped.
I tried something like the following.
Example = ({ props }) => {
return (
<View>
{props.listExample.map(({ id }) => {
let buttonStyle = this.state.pressed ? styles.button : styles.buttonClicked
return (
<React.Fragment key={id}>
<TouchableOpacity
style={buttonStyle}
onPress={() => console.log(id)}>
<Image source={require('example.jpg')} />
</TouchableOpacity>
</React.Fragment>
);
})}
</View>
);
};
But as previously stated, this changed all of the Touchable Opacitys. Is there a way to only change one?
Thanks
Edit - to show entire class
class Page extends Component {
constructor(props) {
super(props)
}
MyButton = ({ onButtonPressed = () => {} }) => {
const [isPressed, setIsPressed] = useState(false);
const onPressed = () => {
setIsPressed(!isPressed);
onButtonPressed();
}
return (<TouchableOpacity style={isPressed ? styles.pressedButton: styles.button}
onPress={onPressed}>
<Image source={require('example.jpg')} />
</TouchableOpacity>
);
}
Example = ({ props }) => {
return (
<View>
{props.listExample.map(({ id }) => {
return (
<MyButton key={id}/>
);
})}
</View>
);
};
render() {
return (
<View style={styles.body}>
<ScrollView>
<View style={styles.column}>
<this.Example props={{ listExample: this.getList()}} />
</View>
</ScrollView>
</View>
);
}
}
It is easier to separate the component inside map to a separate component and then handle style changes on press there
const MyButton = ({ onButtonPressed = () => {} }) => {
const [isPressed, setIsPressed] = useState(false);
const onPressed = () => {
setIsPressed(!isPressed);
onButtonPressed();
}
return (<TouchableOpacity style={isPressed ? styles.pressedButton: styles.button}
onPress={onPressed}>
<Image source={require('example.jpg')} />
</TouchableOpacity>
)
}
so you can use in the map like this
Example = ({ props }) => {
return (
<View>
{props.listExample.map(({ id }) => {
return (
<MyButton key={id} />
);
})}
</View>
);
};

How to add multiple items to a flatlist using textinput?

i'm trying to add items to a flatlist via textinput. at the moment i can only add one item and when i try to add a second it just updates the first item, although the data from the textinput should appear below the previous textinput. i have found a few instances of a similar kind of problem and i know that i probably need to add something to my code but i just can't figure out what. below is my code. i would be grateful for any kind of help :)
function FlatlistComponent({ }) {
const listItems = [];
const [arrayHolder, setArrayHolder] = React.useState([]);
const [textInputHolder, setTextInputHolder] = React.useState('');
useEffect(() => {
setArrayHolder(listItems)
}, [])
const joinData = () => {
listItems.push({ name : textInputHolder });
setArrayHolder(listItems);
}
const FlatListItemSeparator = () => {
return (
<View
style={{
height: 1,
width: "95%",
backgroundColor: '#00678A',
alignSelf: 'center'
}} />
);
}
// Delete note
deleteNote = id => () => {
const filteredData = arrayHolder.filter(item => item.id !== id);
setArrayHolder({ data: filteredData });
}
return (
<View style={styles.MainContainer}>
<FlatList
data={arrayHolder}
width='100%'
extraData={arrayHolder}
keyExtractor={(item) => item.id}
ItemSeparatorComponent={FlatListItemSeparator}
renderItem={({ item }) => <Text style={styles.item} onPress={deleteNote(item.id)}> {item.name} </Text>}
/>
<TextInput
placeholder='Kirjoita uusi'
onChangeText={data => setTextInputHolder(data)}
style={styles.textInputStyle}
underlineColorAndroid='transparent'
clearTextOnFocus={true}
value={listItems}
/>
<TouchableOpacity onPress={joinData} style={styles.button} >
<Text style={styles.buttonText}> + </Text>
</TouchableOpacity>
</View>
);
}
listItems is always an empty array after component re-rendered, you should concat previous arrayHolder with new item:
const joinData = () => {
setArrayHolder([... arrayHolder, {name: textInputHolder }]);
// or use update function
// setArrayHolder(prev => [...prev, {name: textInputHolder }]);
}

How to use ReactNative FlatList ScrolltoIndex properly?

I'm a newbie in react native and I'm running into an issue I didn't manage to solve by myself.
So far this is how I am using it:
export default class Form extends React.Component {
state = {index: 0};
_keyExtractor = (item, index) => item.id;
myscrollToIndex = () => {
this.setState({index: ++this.state.index});
this.flatListRef.scrollToIndex({animated: true,index: this.state.index});
};
_renderItem = ({item}) => (
<View>
<Question {...item} />
<Button label='Next' onPress={this.myscrollToIndex} style={styles.buttonNext}/>
</View>
)
render() {
return (
<View style={styles.container}>
<FlatList
horizontal={false}
ref={(ref) => { this.flatListRef = ref; }}
scrollEnabled
data={this.props.form}
extraData={this.state}
keyExtractor={this._keyExtractor}
renderItem={this._renderItem} />
</View>
);
}
}
I would like to pass the index to myscrolltoindex function but I don't manage to as this.flatListRef gets ruled out when I do that.
Have anyone run into a similar issue ?
Thank you for your time
Use index param as well. And pass the index around.
For e.g _renderItem = ({item, index}) => ( <View> <Question {...item} /> <Button label='Next' onPress={this.myscrollToIndex(index) } style={styles.buttonNext}/> </View> )

Categories

Resources