How to append updated data in previous state in react-redux? - javascript

i am creating a file system App using react-redux and am new to this.
i just want to append the latest data into previous data.
here is my code snippet
action.js
export function getFolderList(url){
const local_url_LOW = isLocal ? lowJSON : url + '?fileFidelity=LOW';
const lowRequest = axios.get(local_url_LOW);
return{
type: GET_FOLDER_LIST,
payload: lowRequest
};
}
reducer.js
export default function (state = INITIAL_STATE, action) {
switch (action.type) {
case GET_FOLDER_LIST:
let data = action.payload.data.Files.File
folderData = Array.isArray(data) ? data : [data];
if (folderData.length) {
for (var i = 0; i < folderData.length; i++) {
lst.push({
id: folderData[i].oid,
name: folderData[i].location,
type: folderData[i].type,
childCount: folderData[i].childCount,
createdDate: folderData[i].createdDate,
lastModifiedDate: folderData[i].lastModifiedDate,
location: folderData[i].location
});
}
return lst;
}
break;
}
return state;
}
so i want to return data like .. state.chlidFiles : lst
so can anyone tell me hoe to do this.
currently when my returning lst array it is creating new state with the previous and the lst arrya, but i want to add this new lst array in previous state's childFiles(or you can say to a specific property)
i have used redux-promise for promises.

I am shooting a blind suggestion here. I would use redux-thunk for async operation, then I would dispatch this and use in my reducer.
action
const getFolderList = (url) =>
( dispatch) => {
const local_url_LOW = isLocal ? lowJSON : url + '?fileFidelity=LOW';
const lowRequest = await axios.get(local_url_LOW);
const data = lowRequest.data.Files.File;
const folderData = Array.isArray(data) ? data : [data];
const payload = folderData.map( file => ( {
id: file.oid,
name: file.location,
type: file.type,
childCount: file.childCount,
createdDate: file.createdDate,
lastModifiedDate: file.lastModifiedDate,
location: file.location,
} ) );
dispatch( {
type: GET_FOLDER_LIST,
payload,
} )
}
reducer
export default function ( state = INITIAL_STATE, action ) {
switch ( action.type ) {
case GET_FOLDER_LIST:
return { ...state, childFiles: [ ...state.childFiles, ...action.payload ] };
default:
return state;
}
}

Related

One of attribute lost data when assign javascript object

I spend a day for investigating this case, but seen no hope.
const Tests = (state = INIT_STATE, action) => {
switch (action.type) {
case GET_TEST_DETAIL:
return {
...state,
test: {},
error: {},
updatingTest: true,
}
case GET_TEST_DETAIL_SUCCESS:
const test = action.payload
console.log(action.payload.shortName)//has data
test["shortName2"] = action.payload.shortName//has data
test["shortName"] = action.payload.shortName//empty string
console.log(test)
return {
...state,
test: test,
updatingTest: false,
}
There is the picture captured when I console.log in firefox, and Edge:
There's nothing wrong.
const action = {
payload: {
shortName: 'I am a shortname'
}
}
const test = action.payload
console.log(action.payload.shortName)//has data
test["shortName2"] = action.payload.shortName//has data
test["shortName"] = action.payload.shortName//empty string
console.log(test)

How do you prevent a javascript object from being converted into a length 1 array of objects?

I'm working on my first solo ReactJS/Redux project and things were going well until I got to a point where I'm using an object in the Redux store that is always supposed to be a single object. When I copy the object from one part of the store (one element of the sources key) to another (the selectedItems key) that object is being stored as an array of length 1, which isn't the data I'm passing in (it's just a single object). I could live with this and just read out of that store variable as an array and just use element 0 except that when I call another method in the reducer to replace that variable in the store, that method stores the new data as a single object! My preference would be to have everything store a single object but I can't figure out how to do that. Anyway, here's some of the reducer code:
const initialState = {
sources: [
{
id: 1,
mfg: 'GE',
system: 'Smart bed',
model: '01',
name: 'GE smart bed'
},
{
id: 2,
mfg: 'IBM',
system: 'Patient monitor',
model: '03',
name: 'IBM patient monitor'
}
],
error: null,
loading: false,
purchased: false,
selectedItem: {}
};
// This is called when a user selects one of sources in the UI
// the Id of the selected sources object is passed in as action.id
// This method creates an array in state.selectedItem
const alertSourceSelect = ( state, action ) => {
let selectedItem = state.sources.filter(function (item) {
return item.id === action.id;
});
if (!selectedItem) selectedItem = {};
return {...state, selectedItem: selectedItem};
};
// When someone edits the selected source, this takes the field name and new value to
// replace in the selected source object and does so. Those values are stored in
// action.field and action.value . However, when the selected source object is updated
// it is updated as a single object and not as an array.
const selectedSourceEdit = ( state, action ) => {
return {
...state,
selectedItem: updateObject(state.selectedItem[0], { [action.field] : action.value })
};
};
const reducer = (state = initialState, action) => {
switch (action.type) {
case actionTypes.ALERT_SOURCE_SELECT: return alertSourceSelect( state, action );
case actionTypes.ALERT_SELECTED_SOURCE_EDIT: return selectedSourceEdit( state, action );
default: return state;
}
Here is the updateObject method (sorry I left it out):
export const updateObject = (oldObject, updatedProperties) => {
return {
...oldObject,
...updatedProperties
}
};
Issue : updateObject is returning object and not array,and you are maintaining selectedItem as an array not object
export const updateObject = (oldObject, updatedProperties) => {
return {
...oldObject,
...updatedProperties
}
};
Solution :
Either return array from updateObject :
export const updateObject = (oldObject, updatedProperties) => {
return [{
...oldObject,
...updatedProperties
}]
};
OR make array of returned object
const selectedSourceEdit = ( state, action ) => {
return {
...state,
selectedItem: [updateObject(state.selectedItem[0], { [action.field] : action.value })]
};
};

How to add a new item into array in an object that's is in the array?

Here is my state
const initState = [
{
id: 1,
criteria: [],
isInclusion: true,
},
{
id: 2,
criteria: [],
isInclusion: true,
},
];
I am trying to add a new object into criteria array with my dispatch
dispatch(
addCriteria({
id: item.id,
type: item.type,
groupId: 1,
})
);
in the reducer I do the following but it doesnt add to an array of an existing group but instead, it adds as a new object to the array with that criteria added in it.
const addReducer = (state = initState, action) => {
switch (action.type) {
case QUERYGROUPS.ADD_CRITERIA: {
const newCriteria = {
id: action.payload.id,
type: action.payload.type,
};
const newState = [
...state,
state.map(group =>
group.id === action.payload.groupId
? {
...group,
criteria: newCriteria,
}
: group
),
];
return newState;
}
}
};
You are adding all items to the state object (also the one you want to modify) with ...state and than you map over it again. This will not work.
You should change your state object to be an object to access the items by reference and not index and use the id as access. This will also be faster(thanks #Yash Joshi ):
const initState = {
1: {
criteria: [],
isInclusion: true,
},
2: {
criteria: [],
isInclusion: true,
},
};
This will let you access and update the state more easily and easier to stay immutable.
This will let you update it like this:
case QUERYGROUPS.ADD_CRITERIA: {
const newCriteria = {
id: action.payload.id,
type: action.payload.type,
};
const newState = {
...state,
[action.payload.groupId]: {
...state[action.payload.groupId],
criteria: [
...state[action.payload.groupId].criteria,
newCriteria
],
}
};
return newState;
To add a new item to it:
const newState = {
...state,
[action.payload.groupId]: {
isInclusion: false,
criteria: [ ],
}
};
Hope this helps. Happy coding.
Try spread operator (es6):
return [
...state,
newCriteria,
]
This will return a new array with the newCriteria object on it.
Here is how I'd do it:
const newCriteria = {
id: action.payload.id,
type: action.payload.type,
};
const newState = state.map(gr => {
if (gr.id !== action.payload.groupId) {
return gr;
}
return {
...gr,
criteria: [...gr.criteria, newCriteria];
}
});
I think you should follow Domino987 approach it will be faster. But if you still wish to continue with your approach. You can try this:
const newState = state.map(item => item.id === newItem.id ? ({ ...item, ...newItem }) : item );
return newState;
Hope this Helps!

React Redux store changes sometimes rerender and sometimes dont

So i am using the fetch API with react / redux to perform API calls to Blockv in my actions and store a returned array of objects in my redux store using a reducer. My array of objects only contains one level of objects (i.e. {id: 9798234982739847, name: 220398402343, etc..}). My table that SHOULD rerender everytime the array is replaced - and I use replace as i want to completely replace the array in the store with the new - only rerenders sometimes and i can see the changes reflected in the store.
I am using thunk middleware. Here is what im working with:
store.js
import { createStore, applyMiddleware } from 'redux'
import thunk from 'redux-thunk'
import { createLogger } from 'redux-logger'
import urbanArcadeReducer from '../reducers/reducers'
import { loadTokens } from '../localStorage'
import { checkAuth } from '../actions/check-auth'
const persistedTokens = loadTokens();
const loggerMiddleware = createLogger();
// Store for Urban Arcade Application
const store = createStore(
urbanArcadeReducer,
persistedTokens,
applyMiddleware(
thunk, // lets us dispatch() functions
loggerMiddleware // neat middleware that logs actions
)
)
// Check for automatic login on page load
store.dispatch(checkAuth(store.getState().access_token))
export default store
reducers.js
function atoms(state = {
receivedAt: null,
atoms: []
}, action) {
switch (action.type) {
case UPDATE_CURR_ATOMS:
var mappedAtoms = action.atoms.map(atom => {
atom = {
id: atom.id,
gameTitle: atom.private.GameTitle,
highscore: atom.private.HighScore,
highscoreOwner: atom.private.HighScoreOwner,
locationName: atom.private.LocationName,
scoreHistory: atom.private.ScoreHistory,
unpublished: atom.unpublished,
author: atom['vAtom::vAtomType'].author,
description: atom['vAtom::vAtomType'].description,
dropped: atom['vAtom::vAtomType'].dropped,
lat: atom['vAtom::vAtomType'].geo_pos.coordinates[1],
lng: atom['vAtom::vAtomType'].geo_pos.coordinates[0],
owner: atom['vAtom::vAtomType'].owner,
template: atom['vAtom::vAtomType'].template,
template_variation: atom['vAtom::vAtomType'].template_variation,
title: atom['vAtom::vAtomType'].title,
when_created: atom.when_created,
when_modified: atom.when_modified
}
return atom
})
return {
...state,
receivedAt: action.receivedAt,
atoms: mappedAtoms
}
default:
return state
}
}
actions.js
import {
fetchRequest,
fetchFailure,
fetchSuccess,
updateNotifier,
updateCurrAtoms } from './action-creators'
import { bringToLogin } from './bring-to-login'
export const UPDATE_CURR_ATOMS = 'UPDATE_CURR_ATOMS'
export function updateCurrAtoms(atoms) {
return { type: UPDATE_CURR_ATOMS, atoms: atoms.atoms, receivedAt: atoms.receivedAt }
}
/**
* Submits request to get all arcade games using Blockv Discover Endpoint(vAtoms)
*
* #returns list of arcade cabinets (vAtoms)
*/
export function getAtoms(params) {
var access_token = params.access_token
var from_refresh = params.from_refresh
var responseCode = ''
var method = 'POST'
var url = 'https://api.blockv.io/v1/vatom/discover'
var headers = {
'Content-Type': 'application/json',
'App-Id': '<App ID>',
'Authorization': 'Bearer ' + access_token
}
var requestBody = {
"scope": {
"key": "vAtom::vAtomType.template",
"value": "<publisher_fqdn>"
},
"filters": [
{
"filter_elems": [
{
"field": "vAtom::vAtomType.template",
"filter_op": "Match",
"value": "<publisher_fqdn>"
}
]
}
],
"return": {
"type": "*",
}
}
var requestBodyJSON = JSON.stringify(requestBody)
// Thunk middleware knows how to handle functions.
// It passes the dispatch method as an argument to the function,
// thus making it able to dispatch actions itself.
return function(dispatch) {
// First dispatch: the app state is updated to inform
// that the API call is starting.
dispatch(fetchRequest())
console.log('Sending get atoms request to Blockv...');
fetch(url, {
method: method,
body: requestBodyJSON,
headers: headers
}).then(response => {
responseCode = response.status
console.log(responseCode)
return response.json()
}).then(data => {
if (responseCode === 401) {
dispatch(bringToLogin("We've logged you out. Please reauthenticate"))
dispatch(fetchFailure('Failed to get atoms'))
} else if (responseCode === 200) {
var atoms = data.payload.results
dispatch(fetchSuccess('Retrieved atoms!')) // Array of template variations
if (from_refresh) {
dispatch(updateNotifier({
isOpen: true,
message: 'Successfully retrieved games!'
}))
}
dispatch(updateCurrAtoms({
atoms: atoms,
receivedAt: Date.now()
}))
}
}).catch(err => {
console.log(err)
dispatch(fetchFailure('Failed to get atoms'))
});
}
}
MyComponent.js
...
class GameStatsModal extends Component {
getDataAtoms = () => {
return this.props.atoms
}
/**
* Props for each row in table
*
* #memberof GameStatsModal
*/
setTrProps = (state, rowInfo, column, instance) => {
return {
style: {
marginBottom: 15
}
}
}
render () {
return (
<OuterContainer>
<StatsContainer>
<InnerContainer>
<img
src={RefreshIcon}
alt="Refresh List"
id="refreshGames"
className='refreshButton'
onClick={
() => {
store.dispatch(getAtoms({
access_token: store.getState().access_token,
from_refresh: true
}))
}
}
/>
<ReactTable
data={this.getDataAtoms()}
className='-highlight -striped gamesTable'
noDataText="Click Refresh to Load Games"
columns={columnsAtoms}
defaultSorted={[{id: 'created', desc: true}]}
getTrProps={this.setTrProps}
getTdProps={this.setTdProps}
pageSize={this.getDataAtoms().length}
showPagination={false}
resizable={false}
/>
</InnerContainer>
</StatsContainer>
</OuterContainer>
)
}
}
const mapStateToProps = state => ({
atoms: state.atoms.atoms
})
export default withRouter(connect(mapStateToProps)(GameStatsModal))
Again i know the updates are being made to store, so my question is am i somehow mutating the previous state somewhere? If not is it possible that since i have multiple dispatch calls being executed in actions, could they be interfering with each other and/or rerendering? not sure where else to look.
Looking forward to any suggestions, thank you!
i think you are mutating the state directly
try this one
switch (action.type) {
case UPDATE_CURR_ATOMS:
var mappedAtoms = action.atoms.map(atom => {
// directly returning the new object
return {
id: atom.id,
gameTitle: atom.private.GameTitle,
highscore: atom.private.HighScore,
highscoreOwner: atom.private.HighScoreOwner,
locationName: atom.private.LocationName,
scoreHistory: atom.private.ScoreHistory,
unpublished: atom.unpublished,
author: atom['vAtom::vAtomType'].author,
description: atom['vAtom::vAtomType'].description,
dropped: atom['vAtom::vAtomType'].dropped,
lat: atom['vAtom::vAtomType'].geo_pos.coordinates[1],
lng: atom['vAtom::vAtomType'].geo_pos.coordinates[0],
owner: atom['vAtom::vAtomType'].owner,
template: atom['vAtom::vAtomType'].template,
template_variation: atom['vAtom::vAtomType'].template_variation,
title: atom['vAtom::vAtomType'].title,
when_created: atom.when_created,
when_modified: atom.when_modified
}
})
return {
...state,
receivedAt: action.receivedAt,
atoms: mappedAtoms
}

Query inside reducer ? redux

How do i write this inside of an reducer to change the state?
doc = {
id:"zf123ada123ad",
name:"examp",
subdoc:{
name:"subdoc examp",
subsubdoc:[{
id:"zcgsdf123zaar21",
subsubsubdoc:[{
id:"af2317bh123",
value: "heyhey" //this value I want to update
}]
}]
}
}
let's say i have an reducer that looks like this
The action.payload look something like this
{
theInputId1: "someId",
theInputId2: "anotherId",
theInputValue: "someValue"
}
export function updateSubSubSubDoc(state = {}, action){
switch(action.type){
case 'UPDATE_THE_SUBSUBSUB':
return {
state.doc.subdoc.subsubdoc.find(x => x.id ==
theInputId1).subsubsubdoc.find(x => x.id == theInputId2).value = theInputValue // just example code for you to understand where i'm going.
}
default:
return state
}
}
What I want to do it update one subsubsub doc in a state that is current
With ES6, this is one way that you could do that:
const initialState = { doc: { subdoc: { subsubdoc: {} } } };
export function doc(state = initialState, action) {
switch (action.type) {
case 'UPDATE_THE_SUBSUBSUB':
const subsubdocIdx = state.doc.subdoc.
subsubdoc.find(s => s.id == action.theInputId1);
const subsubdoc = state.doc.subdoc.subsubdoc[subsubdocIdx];
const subsubsubdocIdx = state.doc.subdoc.
subsubdoc[subsubdocIdx].
subsubsubdoc.find(s => s.id == action.theInputId2);
const subsubsubdoc = state.doc.subdoc.
subsubdoc[subsubdocIdx].
subsubsubdoc[subsubsubdocIdx];
return {
...state,
doc: {
...state.doc,
subdoc: {
...state.doc.subdoc,
subsubdoc: [
...state.doc.subdoc.subsubdoc.slice(0, subsubdocIdx),
{
...subsubdoc,
subsubsubdoc: [
...subsubdoc.slice(0, subsubsubdocIdx),
{
...subsubsubdoc,
value: action.theInputValue,
},
...subsubdoc.subsubsubdoc.slice(subsubsubdocIdx + 1, subsubdoc.subsubsubdoc.length - 1),
],
},
...state.doc.subdoc.subsubdoc.slice(subsubdocIdx + 1, state.doc.subdoc.subsubdoc.length - 1),
]
}
}
}
default:
return state;
}
}
(I haven’t tested this code.)
This is nested the same level as in your example, but you might consider using something like combineReducers to make this a little easier to manage. This is also presupposing you have other actions that create the document chain along the way, and you know these documents exist.
Here's an example how you might be able to do it with combineReducers:
function doc(state = {}, action) {
}
function subdoc(state = {}, action) {
}
function subsubdoc(state = [], action) {
}
function subsubsubdoc(state = [], action) {
switch (action.type) {
case 'UPDATE_THE_SUBSUBSUB':
const idx = state.find(s => s.id == action.theInputId2);
return [
...state.slice(0, idx),
{
...state[idx],
value: action.theInputValue,
},
...state.slice(idx + 1),
];
default:
return state;
}
}
export default combineReducers({
doc,
subdoc,
subsubdoc,
subsubsubdoc,
});
In this example, you don't need action.theInputId1, but you would need to store some reference in the data from the subsubdoc to the subsubsubdoc so that when you're rendering, you can piece it back together. Same with all of the other layers.

Categories

Resources