add property to object in state array with useReducer - reactjs

I have an array of objects stored in state that looks like:
{
"file": {},
"myFsize": 1321,
"fileType": "image/jpeg"
},
{
"file": {},
"myFsize": 999,
"fileType": "image/jpeg"
}
]
I am using useHookReducer to add and remove objects successfully
const [fileSelectedArrax, setfileSelectedArrax] = useReducer(fileSelectReducer, [])
however I want to also add an upload progress to each object as it uploads. It shhould look like this...
{
"file": {},
"myFsize": 1321,
"fileType": "image/jpeg",
"percent": "90"
},
{
"file": {},
"myFsize": 999,
"fileType": "image/jpeg",
"percent": "100"
}
]
This is my reducer:
function fileSelectReducer(state, action) {
switch (action.type) {
case 'add':
console.log(state)
return [...state, action.filex]
case 'adduploadprogress':
//WHAT HERE?
return [...state]
case 'remove':
const update = [...state]
update.splice(update.indexOf(action.file), 1)
document.getElementById(fileattach).value = ''
return update
default:
return state
}
}
My function to invoke this is:
function adduploadprogress({ file, percent }) {
const filex = { file, percent }
console.log(filex)
setfileSelectedArrax({ filex, type: 'adduploadprogress' })
}
I have tried for loops and ternerys to match the object in state to the file being passed in in the action, but nothing seems to work

You can map the array of files (the state), and replace the current file's object with a new object that contains the new percent:
case 'adduploadprogress':
return state.map(f => f.file === action.filex.file ? ({
...f,
percent: action.filex.percent,
}) : f)

Related

What happens when a reducer returns 'state' in React?

If I have a contactReducer that looks like this:
import {
GET_CONTACTS,
DELETE_CONTACT,
ADD_CONTACT,
EDIT_CONTACT,
GET_CONTACT,
} from "../actions/types";
// the state the holds the contacts
const initialState = {
contacts: [
{
id: 1,
name: "John Doe",
email: "john#gmail.com",
phone: "555-555-5555",
},
{
id: 2,
name: "Karen Williams",
email: "karen#gmail.com",
phone: "444-444-4444",
},
{
id: 3,
name: "Henry Johnson",
email: "henry#gmail.com",
phone: "333-333-333",
},
],
contact: {},
testProp: {},
};
export default function (state = initialState, action) {
switch (action.type) {
case GET_CONTACTS:
console.log("get contacts");
return {
...state,
};
case DELETE_CONTACT:
return {
...state,
contacts: state.contacts.filter(
(contact) => contact.id !== action.payload
),
};
case ADD_CONTACT:
let newArray = state.contacts.slice(); // get the current contacts array
newArray.unshift(action.payload); //push on the new contact to the beg of array
return {
...state, //take the existing state..
contacts: newArray,
};
case EDIT_CONTACT:
console.log("trying to edit");
return {
...state,
contacts: state.contacts.map((contact) =>
contact.id == action.id ? (contact = action.payload) : contact
),
};
case GET_CONTACT:
const selectedContact = getSingleContactFromId(state, action.payload);
console.log("look here");
console.log(selectedContact);
return {
...state,
contact: selectedContact,
testProp: { test: "test prop" },
};
default:
console.log("testing action in default");
return state;
}
}
function getSingleContactFromId(state, id) {
var contact;
console.log("get single contact");
for (var i = 0; i < state.contacts.length; i++) {
contact = state.contacts[i];
if (contact.id == id) {
return contact;
}
}
}
What is actually happening when the reducer returns? Where does it return to? For example, I send a dispatch to the reducer like this this.props.addContact(newContact);
But, I don't see that I do anything with the returned object anywhere after this. In another file, is where I grab things from the state, so does return really just mean it is updating the state?
Assuming you use combineReducers, the returned state from a specific reducer will now be the updated state of the state chunk represented by this reducer.
Then, any connected component will receive the new state and will re-render.
This is a high-level description obviously.
More information can be found here: https://react-redux.js.org/using-react-redux/connect-mapstate

TypeError: Cannot read property of undefined when property exists

I'm getting this error when console.log(likes)
TypeError: Cannot read property 'likeCount' of undefined
newState.images[action.data]
console logs an object
{
"id": 26,
"image_title": "a",
"img_url": "h*********,
"created_at": "2019-08-24T17:16:47.116Z",
"updated_at": "2019-08-24T17:16:47.116Z",
"user_id": 1,
"likeCount": "1",
"user": {
"id": 1,
"googleId": null,
"username": "******",
"password": "**********,
"email": "*********",
"created_at": "2019-08-23T21:14:22.356Z",
"updated_at": "2019-08-23T21:14:22.356Z"
},
"comments": []
}
My main objective is to increment/decrement likeCount value in reducer.
this is the reducer. What should i do to the reducer case that will allow me to increment or decrement value. As of now, likeCount does exist but console.log is showing the opposite.
import {
UPLOAD_IMAGE_SUCCESS,
POST_COMMENT_SUCCESS,
DELETE_IMAGE_FAILURE,
FETCH_IMAGES_SUCCESS,
POST_COMMENT,
POST_LIKE,
POST_LIKE_SUCCESS,
DISLIKE_POST_SUCCESS,
DELETE_IMAGE_SUCCESS,
} from '../types';
import { combineReducers } from 'redux';
const initialState = {
images: [],
allIds:[],
byId:{},
likeCount: {},
};
const allIds = (state = initialState.allIds, action ) => {
switch(action.type){
case FETCH_IMAGES_SUCCESS:
return action.images.reduce( (nextState, image) => {
if(nextState.indexOf(image.id) === -1){
nextState.push(image.id)
}
return nextState;
}, [...state])
case UPLOAD_IMAGE_SUCCESS:
console.log(action)
return [action.data.id, ...state];
case DISLIKE_POST_SUCCESS:
const newState = {...state}; // here I am trying to shallow copy the existing state;
const likes = newState[action.data].likeCount
return likes - 1
default:
return state
}
}
const byId = (state = initialState.byId, action) => {
switch(action.type){
case FETCH_IMAGES_SUCCESS:
return action.images.reduce( (nextState, image) => {
nextState[image.id] = image;
return nextState;
}, {...state})
case UPLOAD_IMAGE_SUCCESS:
console.log(action.data)
// what if i want to refer to action.data[key], how would i do this ?
return {
...state,
[action.data.id]: action.data
};
case DISLIKE_POST_SUCCESS:
// here I am trying to shallow copy the existing state;
const newState = {...state}; // here I am trying to shallow copy the existing state;
const likes = newState[action.data].likeCount
return likes - 1
default:
return state
}
}
export default combineReducers({
allIds,
byId,
});
export const getAllImages = (state) => {
const { allIds, byId } = state;
const posts = allIds.map(id => byId[id])
console.log(posts)
return posts
}
error at allIds
You are trying to find the "likeCount" from newState[action.data]. Can you please debug the code check the value of newState[action.data]. I think it's undefined.
Different potential problem: case DISLIKE_POST_SUCCESS: is supposed to return the entire new state, not just that one value.
The reducer is a pure function that takes the previous state and an
action, and returns the next state.
https://redux.js.org/basics/reducers
I think you want something like return {...state, likeCount: likes - 1} here
https://redux.js.org/recipes/using-object-spread-operator

immutable helper updating value in object in array

I'm trying to understand immutable helper in the context of what I'm trying to do.
I'm trying to update the objects inside based on when an onChange event calls the INPUT_CHANGE action. it should add to the formData value not replace it. I tried $add but that didn't work the way I wanted it either.
Also every time a a new input filed is changed it adds a new object with the same updated changes.
Array[index] -> input1 -> INPUT_CHANGE -> update object with key and value of input field as it changes.
inputs=[{key:{key:'input name', value: mew}}]
Array[index] -> input2 -> INPUT_CHANGE -> update object with key and value of input field as it changes.
inputs=[{key:{key:'input name', value: mew}}, {key:{key:'input name', value: mew}}]
and so on ...
const setupState = {
count: 0,
inputs: [{}],
};
export default (state = setupState, action) => {
switch (action.type) {
case INPUT_CHANGE: {
// const formDataArr = state.inputs[count];
return update(state, {
inputs: [{
key: { $set: action.key },
value: { $set: action.value },
}],
});
}
default: return state;
}
};
Kind of confused as to how to use immutable helper for this ?
UPDATE:
This is how the the object should look as user updates each input.
inputs: [
{
"score": {
"key": "score",
"value": "20....",
},
"hits": {
"key": "hits",
"value": "ss..",
}
}
]
Try this.
export default (state = setupState, action) => {
switch (action.type) {
case INPUT_CHANGE: {
// const formDataArr = state.inputs[count];
return update(state, {
inputs: [
// contain origin state.inputs!!!!!!!!!!!!!!!!!!!
...state.inputs,
{
key: { $set: action.key },
value: { $set: action.value },
}],
});
}
default: return state;
}

Redux - Adding element to array nested in an object inside an array

Can't figure out how to properly insert an element into an array, that's inside an object, that's inside an array. Here's an example of my default data for structure:
const defaultState = {
myinbox: [
{
owner: 'John Lennon',
owner_id: 1,
read: true,
messages: [
{
_id: 1,
text: 'When will you be home?',
createdAt: new Date(Date.UTC(2017, 10, 11, 11, 20, 0)),
user: {
_id: 1,
name: 'John Lennon'
}
}
...
I want to add another message when an inbound message comes in. This is what the snippet from my reducer looks like:
const inboxReducer = (state = defaultState, action) => {
switch (action.type) {
case 'ADD_INBOUND_MESSAGE':
return {
...state,
myinbox: [
state.myinbox[action.payload.index]: {
...state.myinbox[action.payload.index],
messages: [
...state.myinbox[action.payload.index].messages,
action.payload.msg
]
}
...state.myinbox,
]
}
default:
return state
}
}
The index of the parent "owner" is passed as the index inside the payload, and the new message is msg in the payload.
I can't figure out how to write this reducer without mutating the original state.
You're mutating the original state when you set myinbox using state.myinbox[action.payload.index]:.
Looks like you're trying to set the state for the index using computed property keys. The syntax for that would be:
myinbox: [
[action.payload.index]: {
...state.myinbox[action.payload.index],
messages: [
...state.myinbox[action.payload.index].messages,
action.payload.msg
]
}
...state.myinbox,
]
This can be done with Immer
const { index, msg } = action.payload;
return produce(state, (draftState) => {
draftState.myinbox[index].messages.push(msg);
});

Updating state with nested array of objects

This is something also called as deep state update. Where I have to update nested state array.
I am implementing a simple redux application. Here I want to update the state which is nested array of object. My reducer function takes state, action. I have to update responses property of state with new value. I tried to map/iterate the state but it isnt working for me. Is there a way to update those specific values and return update state.
const sampleData = [{
"id": 1,
"text": "Hobby",
"answers": [{
"id": 1,
"text": "Chess",
"responses": 5
}]
}];
const action = {
type: "VOTE",
questionId: 1,
answerId: 3
};
This is handleSubmit function I am calling when Submit button is clicked on form.
handleSubmit(e){
const store = createStore(hobbyReducer, hobby); // created store here
var k = (document.querySelector('input[name = "hobby"]:checked').value);
store.dispatch({type:"SUBMIT", text: k, id: 1}); // Dispatching action to reducer
store.subscribe(()=>{
console.log(store.getState());
});
}
Here is reducer part of program:
function hobbyReducer(state, action) {
switch(action.type){
case "SUBMIT":
return{
...state,
answers: state.answers.map(e=> (e.text === action.text && e.answers.id === action.id)?
{ ...answers,
responses: (e.responses+1)} :
hobby )
}
break;
default:
return state;
}
}
initial state = sampleData; // Array object given above
I am unable to update the responses property which is in a nested array
This is the code I wanted to write, after some research I finally did what was required. Although this is not solution in terms of time complexity.
`
case "SUBMIT":
const updatedState = state.map(e =>{
if(e.id === action.id)
return{...e,
answers: e.answers.map(f =>{
if(f.text === action.text){
return{
...f,
...{responses: f.responses + 1},
}
}
return f;
})
}
return e;
})
console.log("updatedstate", updatedState)
return updatedState
Just an error in your map I think:
function hobbyReducer(state, action) {
switch(action.type) {
case "SUBMIT":
return {
...state,
answers: state.answers.map(answer => {
if (answer.text === action.text && answer.id === action.id) {
answer.response = answer.response + 1;
}
return answer;
});
}
default:
return state;
}
}

Resources