React Native Update a Flatlist Item - react-native

I want to update a specific Flatlist row when it is clicked, how can I do this? The solutions I found are all using extraData={this.state}, which is not working in my code.
const [records,setRecords] = useState([]);
//add initial data to records
...
return (
<FlatList
data={records}
keyExtractor={item => item.id}
renderItem={itemData =>
<RecordItem
onSelect={() => {
props.navigation.navigate({...})
}
}
onApply={()=>{
// in my RecordItem these is a button to call onApply(),
and I want to update the Flatlist when I click this button
var res = applyHandler();
return res;
}}
>
</RecordItem>
}
/>)

To update your flatlist item, you just have to update the data it is rendering from, in order to do this, in your onApply function you know the item index with which you can update the record index like:
const [records,setRecords] = useState([]);
//add initial data to records
...
return (
<FlatList
data={records}
keyExtractor={item => item.id}
renderItem={itemData =>
<RecordItem
onSelect={() => {
props.navigation.navigate({...})
}
}
onApply={()=>{
// here you will get reference to record item index,
const itemIndex = itemData.index;
const modRecords = [...records];
modRecords[itemIndex].title = 'NEW TITLE';
// assuming title is what you want to change
// now just update the records using setRecords
setRecords(modRecords);
}}
>
</RecordItem>
}
/>)

Related

React native while adding one item id by onpress it adds all the item at once

can anyone tell me what can i do..i want to change the add button to remove button when one item added and again onpress remove ...add button will come..
function //
const [multipletest, setmultipleTest] = React.useState([]);
function addTest(crypto) {
setmultipleTest((state) => [...state, crypto.id]);}
map list //
{filterdData.map((crypto, index) => (
<View key={index.toString()}>
<TouchableOpacity onPress={() => addTest(crypto)}>
{console.log(crypto, "crypto")}
<Text> Add</Text>
</TouchableOpacity>
</View>
This logic might help
function Example() {
// default state
const [multipletest, setmultipleTest] = React.useState([]);
const add = (crypto) => {
let newData = [...multipletest];
newData.push(crypto.id);
setmultipleTest(newData);
}
const remove = (crypto) => {
let newData = [...multipletest];
newData = newData.filter((e) => e !== crypto.id);
setmultipleTest(newData);
}
// map list
return filterdData.map((crypto, index) => {
// check crypto id already exist or not
const exists = multipletest.includes(crypto.id);
return (
<View key={index.toString()}>
<TouchableOpacity
onPress={() => {
// handle onpress either add or remove as per condition
exists ? remove(crypto) : add(crypto);
}}
>
<Text>{exists ? "Remove" : "Add"}</Text>
</TouchableOpacity>
</View>
);
});
}

Update array React native

I try to update the name in my data array with the value I get from my selected name in my flatlist.
So here I select the name in my flatlist, If I print the selected name in the terminal it print out the selected name, so that works fine.
const [name, setName] = useState();
<FlatList
numColumns={1}
horizontal={false}
data={players}
renderItem={({item}) => (
<View>
<PlayersListCard
playersName={item.name}
deleteIcon="add-circle-outline"
click={() => {
setModalVisiblePlayer(false), setName(item.name);
}}
/>
</View>
)}
/>
And here I try to set the new value (name)
const updateArray = (index) => (e) => {
let newArr = [...data];
newArr[index] = name;
setData(newArr);
};
And here I try to print out the name in my draggable item (renderText)
{
data.map((item, key, index) => (
<Draggable
x={10}
y={200}
key={key}
renderColor="blue"
renderSize={50}
onShortPressRelease={() => setModalVisiblePlayer(true)}
isCircle
textcolor="#000000"
renderText={() => updateArray(index)}
onDragRelease={(e) => {
setItemPosition(key, e.nativeEvent.pageX, e.nativeEvent.pageY);
console.log(item.X);
console.log(item.Y);
}}
/>
));
}
But their is something that I do wrong, can anyone see what?

How to fetch more data when scroll up FlatList react native?

I'm applying lazy load into flatlist. But I only fetch more data when scroll down the lasted item. How can I implement fetch more data when scroll up. I tried onRefresh I dont' know why the list didn't update but the data had changed.
Anyone have ideas for this issues?
<FlatList
style={props.style}
renderItem={renderRow}
data={data}
keyExtractor={(item, index) => String(index)}
onRefresh={onRefresh}
/>
function onRefresh(){
dataFetch = fetchMoreData()
setData([ ...dataFetch, ...data ])
}
Please help me.
Thank you
Base on your description, I wrote the code that can be infinite scrolled when scrolling up, hope this will help.
function App() {
const [records, setRecords] = useState([]);
const [refreshing, setRefreshing] = useState(false);
const onRefresh = async()=>{
const fetchedRecords = await fetchRecords();
const newRecords = fetchedRecords.concat(records);
setRecords(newRecords);
setRefreshing(false);
}
return (
<FlatList
onRefresh={onRefresh}
refreshing={refreshing}
keyExtractor={item => item.id}
data={records}
renderItem={({ item }) => Item(item)}
></FlatList>
);
}
extraData will ensure the update to FlatList - https://reactnative.dev/docs/flatlist#extradata
const [ renderCount, setRenderCount ] = useState(0);
<FlatList
style={props.style}
renderItem={renderRow}
data={data}
keyExtractor={(item, index) => String(index)}
onRefresh={onRefresh}
extraData={renderCount}
/>
function onRefresh(){
dataFetch = fetchMoreData()
setData([ ...dataFetch, ...data ])
setRenderCount(renderCount + 1);
}

How do I setState from toggle checkbox?

The setState is not working when I am trying to use the onToggle from the checkbox to set the state to true.
I have tried to console log the function and it works except that the state won't change.
import CircleCheckBox, { LABEL_POSITION } from 'react-native-circle-checkbox';
const MyTaskItem = (props) => {
return (
<CircleCheckBox
checked={props.taskDone}
onToggle={props.onToggleCheck}
/>
);
};
import MyTaskItem from "../../components/MyTaskItem";
export default function MyTasksScreen() {
const [taskDone, setTaskDone] = useState(false);
const checkDoneHandler = () => {
setTaskDone(true);
console.log(setTaskDone);
};
return (
<View style={styles.screen}>
<MyTaskInput visible={isAddMode} onAddTask={addTaskHandler} onCancel={cancelTaskAdditionHandler} />
<FlatList
keyExtractor={(item, index) => item.id}
data={myTasks}
renderItem={itemData =>
<MyTaskItem
id={itemData.item.id}
onDelete={removeTaskHandler}
title={itemData.item.title}
description={itemData.item.description}
onToggleCheck={checkDoneHandler}
/>}
/>
<AddTodoButton onPress={() => setIsAddMode(true)} />
</View>
);
}
I want to change the state on taskDone to true/false when toggling the checkbox.
I guess you want to change specific task state, so first of all, you need to pass the ID of the task which toggled, so MyTaskItem can be rewritten like this:
const MyTaskItem = (props) => {
const {taskDone, id, onToggleCheck} = props;
return (
<CircleCheckBox
checked={taskDone}
onToggle={()=>onToggleCheck(id)}
/>
);
};
Then you should handle items states in your MyTasksScreen.
Since FlatList is a PureComponent, therefore you need to pass the changing states to the extraData property of the flatlist, to inform item states has changed.
In case you want to change all items done state, you can do like this:
export default function MyTasksScreen() {
const [taskDone, setTaskDone] = useState(false);
const checkDoneHandler = () => {
setTaskDone(true);
console.log(setTaskDone);
};
return (
<View style={styles.screen}>
<MyTaskInput visible={isAddMode} onAddTask={addTaskHandler} onCancel={cancelTaskAdditionHandler} />
<FlatList
keyExtractor={(item, index) => item.id}
data={myTasks}
extraData={taskDone}
renderItem={itemData =>
<MyTaskItem
id={itemData.item.id}
onDelete={removeTaskHandler}
taskDone={taskDone}
title={itemData.item.title}
description={itemData.item.description}
onToggleCheck={checkDoneHandler}
/>}
/>
<AddTodoButton onPress={() => setIsAddMode(true)} />
</View>
);
}
I'd advise you to use TypeScript.
From the code you've added, looks like you're not sending taskDone to your MyTaskItem Component.
Also, try adding console.log(taskDone) after this line: const [taskDone, setTaskDone] = useState(false);

ScrollToEnd after update data for Flatlist

I'm making a chat box with Flatlist. I want to add a new item to data then scroll to bottom of list. I use scrollToEnd method but it did not work. How can I do this?
<FlatList
ref="flatList"
data={this.state.data}
extraData = {this.state}
renderItem={({item}) => <Text style={styles.chatFlatListItem}>{item.chat}</Text>}
/>
AddChat(_chat){
var arr = this.state.data;
arr.push({key: arr.length, chat: _chat});
var _data = {};
_data["data"] = arr;
this.setState(_data);
this.refs.flatList.scrollToEnd();
}
I found a better solution,scrollToEnd() is not working because it is triggered before the change is made to the FlatList.
Since it inherits from ScrollView the best way here is to call scrollToEnd() in onContentSizeChange like so :
<FlatList
ref = "flatList"
onContentSizeChange={()=> this.refs.flatList.scrollToEnd()} />
Thanks #Kernael, just add a timeout like so:
setTimeout(() => this.refs.flatList.scrollToEnd(), 200)
const flatList = React.useRef(null)
<FlatList
ref={flatList}
onContentSizeChange={() => {
flatList.current.scrollToEnd();
}}
data={this.state.data}
extraData = {this.state}
renderItem={({item}) => <Text style={styles.chatFlatListItem}>{item.chat}</Text>}
/>
try this,it works.
My issue here was that scrollToEnd() worked fine on mobile but on web it always scrolled to the top. Probably because I have elements with different size in the FlatList and couldn't define getItemLayout. But thanks to the accepted answer here I solved it. Just with different approach.
const ref = React.useRef<FlatList>();
function handleScrollToEnd(width, height) {
if (ref.current) {
ref.current.scrollToOffset({offset: height});
}
}
<FlatList
ref={ref}
onContentSizeChange={handleScrollToEnd}
/>
This works great on both the mobile and web. Hope it helps to somebody.
Change your code as below. The ref is modified and It's better to use getItemLayout in your FlatList according to this.
AddChat(_chat){
var arr = this.state.data;
arr.push({key: arr.length, chat: _chat});
var _data = {};
_data["data"] = arr;
this.setState(_data);
this.flatList.scrollToEnd();
}
<FlatList
ref={elm => this.flatList = elm}
data={this.state.data}
extraData = {this.state}
renderItem={({item}) => <Text style={styles.chatFlatListItem}>{item.chat}</Text>}
getItemLayout={(data, index) => (
{length: ITEM_HEIGHT, offset: ITEM_HEIGHT * index, index}
)}
/>
Note: Replace the ITEM_HEIGHT with the real value of height of your list items.
Try to use inverted prop on Fatlist itself
Pass your data like this [...data].reverse()
If you are at the middle of list and you need to scroll to end when a new item is added, just use:
ref => flatlistRef.current?.scrollToOffset({offset:0})
seems caused by this line
https://github.com/facebook/react-native/blob/3da3d82320bd035c6bd361a82ea12a70dba4e851/Libraries/Lists/VirtualizedList.js#L1573
when use trigger scrollToEnd, frame.offset is 0
https://github.com/facebook/react-native/blob/3da3d82320bd035c6bd361a82ea12a70dba4e851/Libraries/Lists/VirtualizedList.js#L390
if you wait 1 second, _onContentSize changes and frame.offset is valorized (for ex. 1200 px).
Related post https://github.com/facebook/react-native/issues/30373#issuecomment-1176199466
Simply add a loader before Flatlist renders. For example:
const flatListRef = useRef(null);
const [messages, setMessages] = useState([]);
if(!messages.length){
return <Loader />
}
return (
<View style={styles.messagesContainer}>
<FlatList
ref={flatListRef}
data={messages}
onContentSizeChange={() => {
if (flatListRef.current) {
flatListRef?.current?.scrollToEnd();
}
}}
renderItem={({item, index}) => {
return (
<DisplayMessages
message={item}
index={index}
/>
);
}}
/>
</View>