so i have a store that looks like this:
export class NoteStore {
notes = [
{
id: 1,
name: 'Shopping list',
children: [
{
id: 2,
name: 'Sweet',
items: [
{
id: 3,
name: 'Chocolate',
},
{
id: 4,
name: 'Fudge'
},
{
id: 5,
name: 'Cookies'
}
]
}
]
}
}
i am trying to remove an item from items by pressing a button. so far i have managed to make a remove function that does not throw an error but it doesn't do anything. i have spent so much time with this but i just can't figure out what i need to do to get the removing working properly. this is what i'm working with:
const store = useContext(NoteStore);
function removeItems(id) {
store.notes = store.notes.filter(item => item !== id);
}
//Prints notes content to the screen
const NotesArray = () => {
return store.notes[0].children[0].items.map((item) =>
<View key={item.id} style={{ flexDirection: 'row' }}>
<Text style={styles.note}>{item.name}</Text>
<TouchableOpacity style={{ justifyContent:'center', paddingLeft:5 }} onPress={() => {
removeItems(item.id);
}}>
<Text>
<Icon name={noteDeleteMode ? 'close-circle-outline': null} style={styles.deleteIcon} />
</Text>
</TouchableOpacity>
</View>
)
};
You currently comparing the id with the whole item, not with it's id.
Replace
store.notes = store.notes.filter(item => item !== id);
with
store.notes = store.notes.filter(item => item.id !== id);
(item.id instead of just item)
Probably the issue is that you're saving the reference to the old store.notes somewhere. Fortunately, you don't need to create a new array; you can just modify the old one. Use this:
function removeItems(id) {
store.notes.splice(store.notes.findIndex(item => item.id === id), 1);
}
Related
I have a dynamic form where users can add multiple products. I wonder how I could save the selected products' id.
In the console.log(fields, "fields");, this is where I can see the saved product. So how can I save the selected product id as well?
Any help would be appreciated. Thank you.
Codesandbox: https://codesandbox.io/s/react-hook-form-wizard-form-from-reddit-with-data-ouy64e?file=/src/fieldArray.js:322-4143
const products = [
{
prodName: "Tumbler",
price: 1.5,
size: "500",
colorMap: { Black: 20, Pink: 10, Green: 5 },
id: "aRLMZkiSU7T0lcsPCSsV"
},
{
prodName: "Shirt",
price: 2.0,
size: "L",
colorMap: { Blue: 10, Black: 10 },
id: "uTHIR6OQFRuqP9Drft0e"
},
{
size: "200",
price: 2.0,
colorMap: { Green: 50, Red: 19, Black: 20 },
prodName: "Notebook",
id: "y9ECyZBKp2OBekmWym4M"
}
];
const options = products.map(
(object) =>
object.prodName +
" - " +
object.size +
`${object.cat === "CM" || object.cat === "ML" ? "- " + object.cat : ""}` +
" "
);
console.log(options, "options");
const FieldArray = ({ control, register, setValue, getValues }) => {
const { fields, append, remove, prepends } = useFieldArray({
control,
name: "order"
});
console.log(fields, "fields");
renderCount++;
return (
<div>
<ul>
{fields.map((item, index) => {
console.log(item);
return (
<li key={item.id}>
<Controller
control={control}
name={`order.${index}.product`}
render={({ field: { onChange, value = "", ...rest } }) => (
<Autocomplete
{...rest}
onInputChange={(e, newValue) => {
onChange(newValue);
console.log(newValue, "new value");
}}
inputValue={value}
options={products}
// isOptionEqualToValue={(option, value) =>
// option?.value === value?.value
// }
getOptionLabel={(option) =>
option.prodName + " " + option.size
}
// getOptionLabel={(option) => option?.label ?? ""}
renderInput={(params) => (
<TextField
{...params}
label="Product"
variant="outlined"
fullWidth
/>
)}
/>
)}
/>
);
})}
</div>
);
};
export default FieldArray;
Update
this is the submit button in step1.js
const onSubmit = (data) => {
// action(data);
console.log(data, "d");
const newOrder = [];
data.order.forEach(({ product, variation }) => {
const newVariantion = [];
variation.forEach(({ qty, color }) => {
newVariantion.push({ qty: parseInt(qty), color });
});
newOrder.push({ product, variation: newVariantion });
});
actions.updateAction(data);
console.log(newOrder, "new order");
navigate("/step2", newOrder);
};
Update:
How would I be able to push the product ID inside the newOrder where it matches the productID of the selected product?
Some development on answer from this question:
You can always add useState with a first product (save entire product, not just an id) and then manage everything through onChange:
import {useState} from 'react';
/*...something here...*/
const FieldArray = ({ control, register, setValue, getValues }) => {
const [prod, setProd] = useState({0: product[0]});
/*...something here...*/
{fields.map((item, index) => {
/*...something here...*/
<Autocomplete
onChange={(e, v)=>{console.log(v); setProd({...prod, [index]:v});}}
value={prod[index] || {}}
options={products}
/*...other stuff here...*/
Have a look at what is available in console.log(v) inside onChange.
Also check out difference between inputValue and value here.
Update
If you need multiple products to be saved - prod must be an object with key to represent "fields" item. For example, something like this {0: prod1, 1: prod3, 2: prod11}. Then for value use prod[index] and change setter appropriately. (I've edited code above for this case). There is multiple ways to do this - that's just one from top of my head.
Update 2:
I don't know what you want in onSubmit exactly, so here is an idea and you change it to what you want.
In Step1.onSubmit you can do something like that:
// forEach 2nd argument is an index (counter)
data.order.forEach(({ product, variation }, indx) => {
// some code
newOrder.push({ product, variation: newVariantion, prod: prod[indx] });
// more code
}
So I have an antd table in ReactJS that gets its data from an API call. So in my "account name" column, I have multiple values that are the same and I want to merge them into one row. I've tried looking for questions here and found this codesandbox project that does exactly what I want but when I applied it to my code, it wouldn't work. It drops the cells in my account name column and replaces it with values from the next column (Basically, moving my rows to the left and leaving the last column blank.)
UPDATE: I've added another sandbox link to recreate my problem or where I am at.
const names = new Set();
useEffect(() => {
names.clear();
});
const columns = [
{
key: "1",
title: "Account Name",
dataIndex: "account-name",
rowSpan: 1,
render: (value, row, index) => {
const obj = {
children: value,
props: {}
};
console.log(names.has(value), value);
if (names.has(value)) {
obj.props.rowSpan = 0;
} else {
const occurCount = dataSource.filter(data => data['account-name'] === value).length;
console.log(occurCount);
obj.props.rowSpan = occurCount;
names.add(value);
}
return obj;
},
sorter: (record1,record2) => record1['account-name'].localeCompare(record2['account-name']),
filterDropdown: ({ setSelectedKeys, selectedKeys, confirm }) => {
return (
<Input
autoFocus
placeholder="Search Column..."
value={selectedKeys[0]}
onChange={(e) => {
setSelectedKeys(e.target.value ? [e.target.value] : []);
}}
onPressEnter={() => {
confirm();
}}
onBlur={() => {
confirm();
}}
></Input>
);
},
filterIcon: () => {
return <SearchOutlined />;
},
onFilter: (value, record) => {
return record['account-name'].toLowerCase().includes(value.toLowerCase());
}
},
{
key:"2",
title:'Group ID',
dataIndex:'group-id',
sorter: (record1,record2) => record1['group-id'].localeCompare(record2['group-id']),
width: '18%',
rowSpan: 1,
},
{
key:"3",
title:'Financial Account ID',
dataIndex:'financial-account-id',
sorter: (record1,record2) => record1['financial-account-id'].localeCompare(record2['financial-account-id']),
width: '15%',
rowSpan: 1,
},
{
key:"4",
title:'Industry',
dataIndex:'industry',
sorter: (record1,record2) => record1.industry.localeCompare(record2.industry),
width: '15%',
rowSpan: 1,
},
{
key:"5",
title:'Billing Address',
dataIndex:'billing-address',
rowSpan: 1,
},
];
return (
<div>
<Table
bordered={true}
loading={loading}
columns={columns}
dataSource={dataSource}
pagination={true}
/>
</div>
)
}
When I change the value of obj.props.rowSpan into 0 it drops the whole columns of values. but when I change it into 1, it looks normal but without merging the rows.
Any help would be greatly appreciated I've been trying to fix this for a week.
I'm new in react native , just starting to learn.
I have a FlatList that contain data from array like this one:
const friends = [
{ name: 'Friend #1', age: '26', },
{ name: 'Friend #2', age: '31' },
{ name: 'Friend #3', age: '34' },
];
In the method that return the FlatList I'm added keyExtractor:
return (
<FlatList
keyExtractor={friend => friend.name}
data={friends}
renderItem={({ item }) => {
return ([
<Text style={styles.itemView}>{item.name} - Age {item.age}</Text>
]
);
}} />
);
Still have a warning:
Warning: Each child in a list should have a unique "key" prop.%s%s
Your problem is that you nested Text element inside array
Change it like this:
<FlatList
keyExtractor={friend => friend.name}
data={friends}
renderItem={({ item }) => {
return (
<Text style={styles.itemView}>{item.name} - Age {item.age}</Text>
);
}}
/>
Codesandbox
I am trying to build a section list where the list header is the name of a class (like a school class) and the section list is the assignments that are assigned to that class.
This is my section list component:
<SectionList
sections={classData}
keyExtractor={item => item.class.id}
renderSectionHeader={({ section: classProps }) => {
return (
<Text style={styles.textStyle}>{classProps.name}</Text>
);
}}
renderItem={({ item }) => {
return (
<HomeworkItem
homeworkItem={item}
deleteHomework={() => this.props.removeHomework(item)}
/>
);
}}
/>
This is the code to get classData:
const classData = [];
for (let x = 0; x < this.props.classes.length; x++) {
classData.push({
classProps: this.props.classes[x],
assignments: this.filterAssignments(this.props.classes[x], this.props.homework)
});
}
And the filter assignments function is this:
filterAssignments(c, hw) {
return hw.filter(item => item.className === c.name);
}
I am receiving an error that items.length is undefined at the beginning of the list. The data appears to be working. If anyone knows how I can do this correctly your help would be greatly appreciated.
The react native docs state the following format:
const DATA = [
{
title: 'Main dishes',
data: ['Pizza', 'Burger', 'Risotto'],
},
{
title: 'Sides',
data: ['French Fries', 'Onion Rings', 'Fried Shrimps'],
}
];
this format can be achieved with the following loop:
const classData = this.props.classes.map(item => ({
title: item.name
data: this.filterAssignments(item, this.props.homework)
}));
the classData can now be passed to <SectionList as such:
<SectionList
sections={classData}
In this particular example
const rows = [
{id: 0, text: 'View'},
{id: 1, text: 'Text'},
{id: 2, text: 'Image'},
{id: 3, text: 'ScrollView'},
{id: 4, text: 'ListView'},
]
const extractKey = ({id}) => id
export default class App extends Component {
renderItem = ({item}) => {
return (
<Text style={styles.row}>
{item.text}
</Text>
)
}
render() {
return (
<FlatList
style={styles.container}
data={rows}
renderItem={this.renderItem}
keyExtractor={extractKey}
/>
);
}
}
const styles = StyleSheet.create({
container: {
marginTop: 20,
flex: 1,
},
row: {
padding: 15,
marginBottom: 5,
backgroundColor: 'skyblue',
},
})
provided at http://www.reactnativeexpress.com/flatlist,
Why is the renderItem being assigned a function that take ({item}) instead of just (item)?
Aren't arrow function supposed to work by taking arguments in "()"? Whats the significance of "{}" here? Removing them causes the text in each FlastList item to be empty.
It's a feature of ES6 called Destructuring
The destructuring assignment syntax is a JavaScript expression that
makes it possible to unpack values from arrays, or properties from
objects, into distinct variables.
Source
Basically it's a shortcut syntax to directly get access to a key inside an object passed as parameter of a function.
Example :
const foo = {
bar: "hello world"
};
function basic(arg) {
const bar = arg.bar;
console.log(bar);
};
function destructuring({bar}) {
// no need to define a bar variable here, bar is already avalaible !
console.log(bar);
};
basic(foo);
destructuring(foo);