FlatList update wrong element - reactjs

I have update my flatlist according to last received messages that is rendered componenet of Flatlist. For that reason i coded below code but this code always update last item of flatlist instead of its key.
const [lastMessage, setlastMessage] = useState([
{ key: "1", text: "j1" },
{ key: "2", text: "j2" },
{ key: "3", text: "j3" },
]);
const goBack = (id, data) => { // Here is the change statement that update flatlist.
let result = lastMessage.filter((obj) => {
return obj.key != id;
});
result.push({ key: id, text: data });
setlastMessage(result);
};
Flatlist:
<FlatList
contentContainerStyle={{
width: "100%",
paddingBottom: 200,
}}
keyExtractor={(item) => item.key}
data={lastMessage}
extraData={lastMessage}
renderItem={({ item }) => {
return (
<TouchableOpacity
activeOpacity={0.55}
onPress={() =>
navigation.navigate("ChatScreen", {
ChatRoomID: item.key,
onGoback: goBack, // Here is the reference of function
})
}
>
<ChatCard text={item.text} />
</TouchableOpacity>
);
}}
/>
Chat Screen:
async function handleSend(messages) {
const writes = messages.map((m) => {
chatsRef.add(m);
goBack(ChatRoomID, m["text"]); // i give value to parent from that
});
await Promise.all(writes);
}

Use Array.map instead of filter.
const goBack = (id, data) => {
// Here is the change statement that update flatlist.
let result = lastMessage.map(obj =>
obj.key === id ? { key: id, text: data } : obj
);
setlastMessage(result);
};

try this:
const goBack = (id, data) => {
setlastMessage((value) => {
let result = value.filter((obj) => {
return obj.key != id;
});
return [...result, { key: id, text: data }];
});
};

Related

Ant design Transform component extracting selected data

I need to extract selected/filtered data into the state. I tried everything with onChange and on select change but only I got is the ID of one specific item.
ex. extracted_data = [ '1','2','3'....]
But I need a complete Item with id, name, price, etc...
I am passing data as source data, and after selecting I want to send in the parent component because I need to send it on the server.
Code is below
import { Table, Transfer } from "antd";
import difference from "lodash/difference";
import React, { useState } from "react";
// Customize Table Transfer
const TableTransfer = ({ leftColumns, rightColumns, ...restProps }) => (
<Transfer {...restProps}>
{({
direction,
filteredItems,
onItemSelectAll,
onItemSelect,
selectedKeys: listSelectedKeys,
disabled: listDisabled,
}) => {
const columns = direction === "left" ? leftColumns : rightColumns;
const rowSelection = {
getCheckboxProps: (item) => ({
disabled: listDisabled || item.disabled,
}),
onSelectAll(selected, selectedRows) {
const treeSelectedKeys = selectedRows
.filter((item) => !item.disabled)
.map(({ key }) => key);
const diffKeys = selected
? difference(treeSelectedKeys, listSelectedKeys)
: difference(listSelectedKeys, treeSelectedKeys);
onItemSelectAll(diffKeys, selected);
},
onSelect({ key }, selected) {
onItemSelect(key, selected);
},
selectedRowKeys: listSelectedKeys,
};
return (
<Table
rowSelection={rowSelection}
columns={columns}
dataSource={filteredItems}
size="small"
style={{
pointerEvents: listDisabled ? "none" : undefined,
}}
onRow={({ key, disabled: itemDisabled }) => ({
onClick: () => {
if (itemDisabled || listDisabled) return;
onItemSelect(key, !listSelectedKeys.includes(key));
},
})}
/>
);
}}
</Transfer>
);
const leftTableColumns = [
{
dataIndex: "name",
title: "Name",
},
{
dataIndex: "price",
title: "Price (€)",
},
{
dataIndex: "discount",
title: "Discount (%)",
},
];
const rightTableColumns = [
{
dataIndex: "name",
title: "Name",
},
{
dataIndex: "price",
title: "Price",
},
];
const App = ({ data, func }) => {
const mockData = data.map((item) => ({
key: item.id.toString(),
name: item.name,
price: item.price,
discount: item.discount,
}));
const originTargetKeys = mockData.map((item) => item.key);
const [targetKeys, setTargetKeys] = useState(originTargetKeys);
const [selected, setSelected] = useState([]);
const onSelectChange = (e) => {
setSelected(e);
};
const onChange = (e) => {
setTargetKeys(e);
func(selected);
};
return (
<>
<TableTransfer
dataSource={mockData}
targetKeys={targetKeys}
disabled={false}
showSearch={true}
onChange={onChange}
onSelectChange={onSelectChange}
filterOption={(inputValue, item) =>
item.name.indexOf(inputValue) !== -1
}
leftColumns={leftTableColumns}
rightColumns={rightTableColumns}
/>
</>
);
};
export default App;

React Card Example issue - Card is replaced instead of being appended to a list of cards in another column

I have been persistently working on this problem where the goal is to drag a card form 'Column 1' and copy that into another column say 'Column 2'.
Now when my first card is dragged and drop it into 'Column 2, the card is accordingly added to that column, but when I drag another card and drop into 'Column 2' instead of being appended it just replaces the existing card with itself.
I have been debugging the state, but the issue still persists. I haven't gotten a clue what am I doing wrong here?
Here's my code
// Card Component
function Card({ id, text, isDrag }) {
const [, drag] = useDrag(() => ({
type: "bp-card",
item: () => {
return { id, text}
},
collect: monitor => ({
isDragging: !!monitor.isDragging(),
}),
canDrag: () => isDrag
}));
return (
<div
className='card'
ref={drag}
style={{
cursor: isDrag ? 'pointer' : 'no-drop'
}}
>
{text}
</div>
)
}
// Column Component
function Column({ title, children, onCardDropped }) {
const [, drop] = useDrop(() => ({
accept: "bp-card",
drop: item => {
onCardDropped(item);
}
}));
return (
<div className="flex-item" ref={title === 'Column 2' ? drop : null}>
<p>{title}</p>
{children.length > 0 && children.map(({ id, text, isDrag }) => (
<Card
key={id}
id={id}
text={text}
isDrag={isDrag}
/>
))}
</div>
)
}
// Main App
function App() {
const [cards] = useState([
{ id: 1, text: 'Card 1', isDrag: true },
{ id: 2, text: 'Card 2', isDrag: true },
]);
const [columns, setColumns] = useState([
{
id: 1,
title: 'Column 1',
children: cards
},
{
id: 2,
title: 'Column 2',
children: []
},
]);
const onCardDropped = ({ id, text }) => {
// let card = null;
const targetColumnId = 2;
const transformedColumns = columns.map(column => {
if (column.id === targetColumnId) {
return {
...column,
children: [
...column.children,
{ id, text }
]
}
}
return column;
});
setColumns(transformedColumns);
}
return (
<DndProvider backend={HTML5Backend}>
<div className='flex-container'>
{columns.map((column) => (
<Column
key={column.id}
title={column.title}
children={column.children}
onCardDropped={onCardDropped}
/>
))}
</div>
</DndProvider>
);
}
Any help is highly appreciated. Thanks.
You need to consider the previous state using the callback of the set state method. It starts to work after changing the onCardDropped as below.
const onCardDropped = ({ id, text }) => {
// let card = null;
const targetColumnId = 2;
setColumns((prevColumns) =>
prevColumns.map((column) => {
if (column.id === targetColumnId) {
return {
...column,
children: [...column.children, { id, text }]
};
}
return column;
})
);
};
It's always a good idea to use the state from the callback method as opposed to using the state object directly which might be stale.
Working Demo

Material-Table with React: how to use star rating in the cell?

I would like to style my cell's rating into star by using Material-Table,
like the original Material-UI provided:
https://material-ui.com/components/rating/
Is it possible to use in Material-Table? I cannot find document related to this...just for the style for background, color, etc., not for writing functions in cell style.
https://material-table.com/#/docs/features/styling
thanks a lot!
You can use material-table's custom edit component to render the mui Rating component.
Full Working demo
Sample code snippet of columns array
const columns = propValue => [
{ title: "Id", field: "id" },
{ title: "First Name", field: "first_name" },
{
title: "Rating",
field: "rating",
render: rowData => {
return <Rating name="hover-feedback" value={rowData.rating} readOnly />;
},
editComponent: props => (
<Rating
name="hover-feedback"
value={props.value}
onChange={(event, newValue) => {
props.onChange(newValue);
}}
/>
),
cellStyle: {
backgroundColor: "#039be5",
color: "#FFF"
},
width: "30%"
}
];
Component
class App extends Component {
tableRef = React.createRef();
propValue = true;
state = { data: [] };
componentDidMount() {
const query = 0;
let url = "https://reqres.in/api/users?";
url += "per_page=" + query.pageSize;
url += "&page=" + (query.page + 1);
fetch(url)
.then(response => response.json())
.then(result => {
console.log("result", result);
this.setState({
data: result.data.map(d => ({ ...d }))
});
});
}
render() {
return (
<div style={{ maxWidth: "100%" }}>
<MaterialTable
icons={tableIcons}
tableRef={this.tableRef}
columns={columns(this.propValue)}
editable={{
onRowUpdate: (newData, oldData) =>
new Promise((resolve, reject) => {
console.log("newData", newData);
console.log("oldData", oldData);
const dataUpdate = [...this.state.data];
const index = oldData.tableData.id;
dataUpdate[index] = newData;
this.setState({ data: dataUpdate }, () => {
console.log("xx", this.state.data);
resolve(this.state);
});
})
}}
data={this.state.data}
title="Remote Data Example"
options={{ tableLayout: "fixed" }}
/>
<button
onClick={() => {
this.tableRef.current.onQueryChange();
}}
>
ok
</button>
</div>
);
}
}

Deleting/Updating nested object using React Hooks

I am relatively new to react-native programming language. I've been trying to make my first standalone application, which of course is To Do List app.
I have to do items under their own respective categories. The source of these to do items is a predefined object as shown below.
const [listData, setListData] = useState([
{
id: 1,
title: "Work",
data: [
{
text: 'MSD',
key: 1
},
{
text: 'Cosmo',
key: 2
},
{
text: 'Jackson',
key: 3
},
]
},
{
id: 2,
title: "Home",
data: [
{
text: 'Gym',
key: 4
},
{
text: 'Dinner',
key: 5
},
{
text: 'React',
key: 6
},
]
},
]);
This data is defined inside the main function App(). The data renders perfectly on screen with the below code.
Home Screen Image
return(
<View style={styles.container}>
<Header />
<SectionList
sections={listData}
keyExtractor={(item, index) => item + index}
renderSectionHeader={({
section: { title } }) => (
<Text style={styles.header}>{title}</Text>
)}
renderItem={({ item }) => <Item data={item.text} dataID={item.key} onDeleteHandler={onDeleteHandler}/>}
/>
</View>
)
Item component code is -
const Item = ({ data, dataID, onDeleteHandler }) => {
const [checked, setChecked] = React.useState(false);
return(
<View style={styles.itemGroup}>
<TouchableOpacity style={styles.item} onPress={() => {
if (checked == true){
setChecked(false)
}
else{
setChecked(true)
}
}}>
<View>
<Text style={checked === true ? styles.toDoTextStrikeThrough : styles.toDoText}>{data}</Text>
</View>
</TouchableOpacity>
<TouchableOpacity onPress={() => onDeleteHandler(dataID)}>
<View style={styles.checkBoxAndDeleteStyle}>
<Entypo name="cross" color='#004d4d' size={18}/>
</View>
</TouchableOpacity>
</View>
)
};
export default Item;
The bit where i'm stuck at is the delete button which invokes the below function.
const onDeleteHandler = (key) => {
const parentArray = [...listData]
for (let i = 0; i < parentArray.length; i++)
{
let paDataItems = {...parentArray[i]}.data
const dataArray = [...paDataItems]
for (let k = 0; k < dataArray.length; k++){
if (dataArray[k].key === key){
dataArray.splice(k,1)
break
}
}
console.log(dataArray)
}
}
dataArray shows all the correct objects. I am not able to figure out how to update this back to the main dataset.
I've tried the below two methods and both result in weird behaviours.
setListData((paDataItems) => [...paDataItems,{data:dataArray}])
setListData([{...parentArray[i]}, {data:dataArray}])
How should I write this function so I can delete the selected item?
I'm guessing I should be able to figure out how to update the dataset once I have a solution to the above problem.
Any help would be appreciated. Thank you.
You can map the groups/categories and filter the data array:
const onDeleteHandler = key => {
const newData = listData.map(category => ({
...category,
data: category.data.filter(item => item.key !== key)
})
);
setListData(newData);
};
It may be more correct to use a functional state update since the next state depends on the previous state having an task item deleted correctly in the case that multiple state updates are queued up within a single render cycle:
const onDeleteHandler = key => {
setListData(listData => listData.map(category => ({
...category,
data: category.data.filter(item => item.key !== key)
})
));
};
let data = [{
id: 1,
title: "Work",
data: [{
text: 'MSD',
key: 1
},
{
text: 'Cosmo',
key: 2
},
{
text: 'Jackson',
key: 3
},
]
},
{
id: 2,
title: "Home",
data: [{
text: 'Gym',
key: 4
},
{
text: 'Dinner',
key: 5
},
{
text: 'React',
key: 6
},
]
},
];
const onDeleteHandler = key => {
const newData = data.map(category => ({
...category,
data: category.data.filter(item => item.key !== key)
})
);
return newData;
};
console.log(onDeleteHandler(3))
You can simplify your onDeleteHandler function like this:
const newData = listData.map(group => {
return {
...group,
data: group.data.filter(task => task.key !== key)
}
})
setListData(newData);

ReactJS Render Table for each value on Array

I have a MultiSelect and a React Table..
the Select stores the values into value Array..
The way it is now i´m able to select ONE option and the table displays the data correctly. But, i´m looking to render a table for each selected option. How could i achieve something like this?
handleSelectChange (value) {
console.log('You\'ve selected:', value);
this.setState({ value: value }, () => this.fetchTable());
}
fetchTable() {
const url = 'http://localhost:8000/issues/from/';
const value = this.state.value;
const string = url+value;
fetch(string)
.then(function(response) {
return response.json();
})
.then((myJson) => this.setState({data: myJson.issues}));
}
componentDidMount() {
this.fetchData();
}
render() {
const filteredResult = this.state.boards.map(item => (
{
value: item.key,
label: item.name,
}
));
const filteredResult1 = this.state.data.map(item => (
{
name: item.fields,
id: item.id,
key: item.key,
}
));
return (
<div>
<Select
closeOnSelect={!stayOpen}
disabled={disabled}
multi
onChange={this.handleSelectChange}
options={filteredResult}
placeholder="Select Assignee(s)..."
removeSelected={this.state.removeSelected}
rtl={this.state.rtl}
simpleValue
value={value}
/>
<ResponseTable data={filteredResult1} />
</div>
);
}
}
How does your ResponseTable component look like? I guess you can just use the map function to loop and display the table rows. Sth like this:
const data = [{name: 'Test 1', id: 1, key: 'key_1'}, {name: 'Test 2', id: 2, key: 'key_2'}, {name: 'Test 3', id: 3, key: 'key_3'}];
_renderTableBody = () => {
return data.map((item) => (
return (
<TableRow>
<TableCell>item.name</TableCell>
<TableCell>item.id</TableCell>
<TableCell>item.key</TableCell>
</TableRow>
)
))
}
Then inside your render function, you can just replace this
<ResponseTable data={filteredResult1} />
into the code like this:
{this._renderTableHead()} // same method as _renderTableBody() to generate the table head title
{this._renderTableBody()}
Hope this can help!
Just keep some dummy key in state which as empty array initially. It will push the selected value of select option in to it. like below
constructor(props){
this.state = {
selectedValues: []
}
}
Alter your handleSelectChange like below. It needs to update the current selected value in this array
handleSelectChange (value) {
console.log('You\'ve selected:', value);
//this.setState({ value: value }, () => this.fetchTable());
let currentSelectedValue = this.state.selectedValues.filter(selected => selected == value)
//it will return a value if it is found otherwise empty array will be returned
if(currentSelectedValue.length == 0){
let updatedSelectedValue = this.state.selectedValues.push(value)
this.setState({ selectedValues: updatedSelectedValues }, () => this.fetchTable());
}
}
removeSelected (value) {
console.log('You\'ve selected:', value);
//this.setState({ value: value }, () => this.fetchTable());
let currentSelectedValue = this.state.selectedValues.filter(selected => selected !== value) //this will delete the removed option from array
this.setState({ selectedValues: currentSelectedValue }, () => this.fetchTable());
}
fetchTable() {
if( this.state.selectedValues.length > 0 ){
this.state.selectedValues.map((value)=>{
const url = 'http://localhost:8000/issues/from/';
const string = url+value;
fetch(string)
.then(function(response) {
return response.json();
})
.then((myJson) => this.setState({data: [...this.state.data, myJson.issues]})); //use spread operator to combine the result of each selectedValue
});
}
}
render() {
const filteredResult = this.state.boards.map(item => (
{
value: item.key,
label: item.name,
}
));
const filteredResult1 = this.state.data.map(item => (
{
name: item.fields,
id: item.id,
key: item.key,
}
));
return (
<div>
<Select
closeOnSelect={!stayOpen}
disabled={disabled}
multi
onChange={this.handleSelectChange}
options={filteredResult}
placeholder="Select Assignee(s)..."
removeSelected={this.state.removeSelected}
rtl={this.state.rtl}
simpleValue
value={value}
/>
this.state.data.map((item) => { // item here will hold the json object of { id: item.id, key: item.key, name: item.fields }
<ResponseTable data={item} />
})
</div>
);
}
}

Resources