I have a problem with my code. I currently have some data like the one below;
users: [
{
name: 'bolu',
features: ['Tall'],
},
{
name: 'cam',
features: ['Bearded', 'Short'],
},
],
};
What I am trying to do is delete/remove a single feature - for example if I pass in 'short' into my redux action. I'd like for it (the 'Short' text) to be removed from the features array. I currently have my redux action set up this way:
export interface UsersDataState {
name: string,
features: Array<string>,
}
export interface UsersState {
users: UsersDataState[];
}
const initialState: UsersState = {
users: [],
};
export const usersSlice = createSlice({
name: 'users',
initialState,
reducers: {
removeUser: (state, action: PayloadAction<string>) => {
const removedUsers = state.users.filter((user) => user.features.indexOf(action.payload));
state.users = removedUsers;
},
},
});
So here I am passing in the value in (action.payload is the value being passed in). When this action is dispatched, I want to remove just the word that is passed in from the features array. I hope this is clearer now.
This doesn't work for some reason and I am unable to figure out why. Any help would be appreciated please, thank you.
You need to copy the objects on users and filter on features.
Here an example:
var users = [{
name: 'bolu',
features: ['Tall'],
}, {
name: 'cam',
features: ['Bearded', 'Short'],
}];
const payload = "Short";
const newUsers = users.map(user => ({ ...user,
features: user.features.filter(f => f != payload)
}));
console.log(newUsers);
Currently you are filtering users array but you should be filtering nested features array.
Try this
const removedUsers = state.users.map(user => {
return {...user, features: user.features.filter(feature => feature !== action.payload)};
})
Related
I'm trying to save objects to an array, but I can't do it, the old state is deleted. I have two states in my component, from two different forms, the first form is just text and I get the data by "handleChange", but the second form is several objects that I want to store in an array that I get by "handleChangeArray".
const [formCompra, setFormCompra] = useState({
name: '',
lastName: '',
items: []
});
const [restForm, setRestForm] = useState();
const handleChage = (e) => {
const { name, value } = e.target;
setFormCompra({
...formCompra,
[name]: value
})
}
const handleChangeArray = (e) => {
const { name, value } = e.target;
setRestForm({
...restForm,
[name]: value
})
}
const handleSubmit = () => {
let newData = {
name: formCompra.name,
lastName: formCompra.lastName,
items: [...formCompra.items, restForm] //probably the error is here
}
console.log(newData)
}
As I mention, it is not possible to save the data in the array, I appreciate any help.
You can use the current state to set a new value, keeping all other values:
setState((current) => ({
...current,
key: newValue
}));
I think the issue may be that spread syntax only shallow copies the array, so in
const handleSubmit = () => {
let newData = {
name: formCompra.name,
lastName: formCompra.lastName,
items: [...formCompra.items, restForm] //probably the error is here
}
items is a copy of an array that points to all the original objects.
try
let newData = {
name: formCompra.name,
lastName: formCompra.lastName,
items: [...formCompra.map(x=>{...x}), {...restForm}] //probably the error is here
}
I have a reducer for adding comment as follow
addComment: {
reducer: (state,action)=>{
const { newId, comment } = action.payload
console.log(newId)
// functions that create an new comment entry with the newID and update state
},
prepare: (input) =>{
return {
payload: {
...input,
newId: nanoid(),
}
}
}
If I call dispatch within my app as follow, the console.log shows the id generated by nanoid(). So far so good.
dispatch(addComment({comment: comment}))
However if I call the reducer within another reducer using the caseReducers, console.log returns undefined
commentSlice.caseReducers.addComment(state,{
payload: {
comment : comment
}
})
How do I get the prepare callback to run when using caseReducers.
Based on the docs it looks like if you are to invoke using the caseReducer way, you need to include the "type":
const slice = createSlice({
name: 'test',
initialState: 0,
reducers: {
increment: (state, action: PayloadAction<number>) => state + action.payload,
},
})
// now available:
slice.actions.increment(2)
// also available:
slice.caseReducers.increment(0, { type: 'increment', payload: 5 })
I'm trying to build a simple budgeting app.
Whenever I insert this model into my app. I get a proxy for the expenses. Where is the flaw in my thinking?
I have an action on the Budget.js
when I print it in the useEffect this is what console.log outputs for the expenses a proxy.
I'm expecting it to print the actual data from the initial state.
React.useEffect(() => {
budget.addDummyData()
console.log(budget.expenses)
}, [])
[[Handler]]: Object
[[Target]]: Array(0)
[[IsRevoked]]: false
//////////////////////////////////////////////////////////////////////
//SubCategory
const SubCategory = types
.model('SubCategory', {
id: types.maybeNull(types.string, ''),
name: types.maybeNull(types.string, ''),
amount: types.maybeNull(types.number, 0)
})
const SubCategoryStore = types.model({ subCategory: types.optional(SubCategory, {}) })
export default SubCategoryStore
/////////////////////////////////////////////////////////////////////////
//Category.js
const Category = types
.model('Category', {
id: types.maybeNull(types.string, ''),
name: types.maybeNull(types.string, ''),
subCategories: types.array(SubCategory)
})
const CategoryStore = types.model({ category: types.optional(Category, {}) })
export default CategoryStore
///////////////////////////////////////////////////////////////
// Budget
const Budget = types
.model('Budget', {
totalIncome: 200,
expenses: types.array(Category)
// incomes: types.optional(types.array(Category), [])
}).actions({
addDummyData() {
self.expenses.push(initialStateExpenses)
}
})
const BudgetStore = types.model({ budget: types.optional(Budget, {}) })
export default BudgetStore
const initialStateExpenses = {
id: '123',
name: 'Food',
subCategories: [
{
id: '1314',
name: 'Grocery',
amount: 250
},
{
id: '1442',
name: 'Restaurants',
amount: 50
}
]
}
expenses is of type Category[], you are passing an object. I assume you want to set the expenses from subCategories. If so you can try this
addDummyData() {
initialStateExpenses.subCategories.forEach(ex => self.expenses.push(ex))
}
or
addDummyData() {
self.expenses = initialStateExpenses.subCategories
}
A better approach would be to pass the initialStateExpenses via args to the addDummyData function so your model doesn't depend on external variables
addDummyData(initialStateExpenses) {
initialStateExpenses.subCategories.forEach(ex => self.expenses.push(ex))
}
or
addDummyData(initialStateExpenses) {
self.expenses = initialStateExpenses.subCategories
}
then use it like
budget.addDummyData(initialStateExpenses)
Trying to create a really simple redux todo, almost there but got stuck on one thing.
export const completeTodo = (todo) => ({
type: 'COMPLETE_TODO',
data: {
name: todo,
complete: !todo.complete
}
})
however, struggling to get the reducer working as I can't work out how to determine the exact object im working on
reducer:
case 'COMPLETE_TODO': {
const chore = { ...state.chores, complete: action.data.complete}
return { ...state.chores, chore };
}
and initialState is:
const initialState = {
chores: [{name: 'cleaning', complete: false}]
}
obviously when i click my button is should be wired up so it can change the complete boolean to the opposite but only for that one todo
Since you have an array you need to replace it with a new one
case 'COMPLETE_TODO': {
return {
...state,
chores: state.chores.map(chore =>
chore.name === action.data.name
? {...chore, complete: true /* or !chore.complete if you want toggle like behaviour*/}
: chore)
};
}
and in your action creator
export const completeTodo = (todo) => ({
type: 'COMPLETE_TODO',
data: {
name: todo.name // assumning names are unique
}
})
Lets say I have a reducer which is like :
const initialState = [
{
accessToken: null,
isLoggedIn: false,
}
]
export default function my_reducer(state = initialState, action) {
switch (action.type) {
case LOGIN:
return state.merge(user: action) ---> how to handle this
and the output should be like:
[
{
accessToken: null,
isLoggedIn: false,
user: {
name: 'some name',
email: 'some email'
}
]
In action I am getting a array which I am providing by doing JSON.stringify(response)
previous data should not be changed and new data should be updated
The ES6 Way
To create a new object with the state in ES6 we can use the spread operator. Like this
...
case ActionType.SUCCESS_GET_DATA : {
let newState = { ...state, [action.uniqueKey]: action.payload };
return state.merge(newState);
}
...
I did the uniqueKey part as a variable because you will want a unique name for your state.
IMO this syntax is much easier to understand than the Object.assign
You can use Object.assign() function:
var state = {
accessToken: null,
isLoggedIn: false,
};
var user = {
name: 'some name',
email: 'some email'
};
var newState = Object.assign({}, state, {user});
console.log(newState);
First I see that your state is actually an array, but I think you would need an object right?
So it would be:
const initialState = {
accessToken: null,
isLoggedIn: false,
}
(requires Babel) So with spread operator you can:
return {
...initialState,
user: {
name: '...',
surname: '...'
}
};
Or if you do not transpile via Babel alike:
return Object.assign({}, initialState, {
user: {
name: '...',
surname: '...'
}
});
Using ES6 spread syntax
...
case 'ACTION_TYPE_A': {
return { ...state, action.key: action.value };
}
...
This will return the merged state by updating the 'key' if it exists in the original state.
Everything according to new Es6 format : )
A total addToDo reducer function where the data is appended to the previous state. And you get the output of a new state data : )
export const addToDo = (state, action) => {
const { name, email, phone, image,key } = action;
var data = [...state.data];
var newData = {
name: name, email: email, phone: phone, image: image,key:key
}
data.push(newData)
return(
state.merge({
data : data
})
)};
Happy coding.