I have this mongoose Schema
const storeSchema = mongoose.Schema({
name: {
type: String,
required: true,
},
fruits: {
type: [String],
required: true,
},
});
On my client side I'm doing this for the name input, and it's working so far.
const [store, setStoreData] = useState({
store: '',
fruits: [''],
});
const dispatch = useDispatch();
const handleSubmit = (e) => {
e.preventDefault();
dispatch(addStore(store));
};
...
<TextField
label='Store Name'
variant='outlined'
value={store.name}
onChange={(e) =>
setStoreData({
...store,
name: e.target.value,
})
}
/>
I tried to imitate the behaviour but I think it has to be more complex. Problem is I wanted to have 2 or 3 of these "input select" for each fruit. And each would have multiple int choices like 1, 2, 3. And depending on the target value, push into the fruits array that number of fruit. This is what I had.
<Select
value={store.fruits}
label='Fruits'
onChange={(e) =>
setStoreData({
...store,
fruits: e.target.value,
})
}>
<MenuItem value={1}>1</MenuItem>
<MenuItem value={2}>2</MenuItem>
<MenuItem value={3}>3</MenuItem>
</Select>
So ideally I would have 3 of these inputs, one for each fruit like bananas and apples, and the number the client chose would be the number of entries that the number banana would be inserted into the array.
Example: A select that says I have 3 (the number 3) bananas. And I would get in the database this.
{
"name": store
"fruits": ["banana", "banana", "banana"]
}
I am using @mui/material for react.
CodePudding user response:
A good solution might be to change the structure of your state to a more input friendly one, then transform this value to one that matches your store schema upon submission.
Your state declaration might look something like this;
const [store, setStoreData] = useState({
name: "",
fruits: {
apple: 0,
banana: 0,
orange: 0
},
});
You can then have a shared method to update the fruits value in your state.
const handleChangeFruit = (name, value) => {
setStoreData(store => ({
...store,
fruits: {
...store.fruits,
[name]: value
}
}));
}
Then your fruit Select
input becomes:
<Select
value={store.fruits.apple}
label='apple'
onChange={(e) =>
handleChangeFruit('apple', parseInt(e.target.value))
}>
<MenuItem value={1}>1</MenuItem>
<MenuItem value={2}>2</MenuItem>
<MenuItem value={3}>3</MenuItem>
</Select>
To transform this state to your schema shape, you can do something like below;
const getStoreData = () => {
const fruits = Object.entries(store.fruits).reduce(
(acc, cur) => acc.concat(new Array(cur[1]).fill(cur[0])),
[]
);
return {
name: store.name,
fruits,
};
};
Your handleSubmit
method now becomes:
const handleSubmit = (e) => {
e.preventDefault();
dispatch(addStore(getStoreData()));
};
This should do the trick.
To get extra smart points, you can even generate the Select
fields dynamically like so:
Object.keys(store.fruits).map((fruit) => (
<Select
key={fruit}
value={store.fruits[fruit]}
label={fruit}
onChange={(e) => handleChangeFruit(fruit, parseInt(e.target.value))}
>
<MenuItem value={1}>1</MenuItem>
<MenuItem value={2}>2</MenuItem>
<MenuItem value={3}>3</MenuItem>
</Select>
));