I have an object with some data in the vuex store that I will need to use in a component,I have accessed the data with a getter in the component. I am trying to save the initial value of that object in a variable in a way that it doesn't change/update when the data in the store changes, this will allow me to do a comparison condition.
the code is below, the object accessed by 'getSelectedLocation' is what I need the original value of kept.
data() {
return {
toggle: this.getSelectedLocation?.local,
}
},
computed: {
...mapGetters({ getUser: 'user/getUser', getSelectedLocation: 'support/getSelectedLocation' }),
},
methods: {
async updateUsersLocation() {
const usersCol = new UsersCollection(this.$fire.firestore)
const id = this.getUser.id
const location = this.getSelectedLocation
return await usersCol.updateUsersLocation({ id, location })
},
},
}
You need two things. First to make a copy of that object, so it cannot change by reference the initial object, then to freeze it, so it cannot change itself.
const copy = Object.assign({}, this.getSelectedLocation())
Object.freeze(copy)
Related
I want to update value of one object only but updating value of one Object, Updates the value for all objects.
let default = {
name: '',
age: ''
}
this.state = {
values: Array(2).fill(default)
}
updateName (event) {
let index = event.target.id,
values = this.state.values;
values[index].name = event.target.value;
this.setState ({
values: values
});
}
There are four significant problems in that code.
You're using the same object for all entries in your array. If you want to have different objects, you have to create multiple copies of the default.
You're calling setState incorrectly. Any time you're setting state based on existing state (and you're setting values based, indirectly, on this.state.values), you must use the function callback version of setState. More: State Updates May Be Asynchronous
You can't directly modify the object held in this.state.values; instead, you must make a copy of the object and modify that. More: Do Not Modify State Directly
default is a keyword, you can't use it as an identifier. Let's use defaultValue instead.
Here's one way you can address all four (see comments):
// #4 - `default` is a keyword
let defaultValue = {
name: '',
age: ''
};
this.state = {
// #1 - copy default, don't use it directly
values: [
Object.assign({}, defaultValue),
Object.assign({}, defaultValue),
] // <=== Side note - no ; here!
};
// ....
updateName(event) {
// Grab the name for later use
const name = event.target.value;
// Grab the index -- I __don't__ recommend using indexed updates like this;
// instead, use an object property you can search for in the array in case
// the order changes (but I haven't done that in this code).
const index = event.target.id;
// #2 - state updates working from current state MUST use
// the function callback version of setState
this.setState(prevState => {
// #3 - don't modify state directly - copy the array...
const values = prevState.values.slice();
// ...and the object, doing the update; again, I wouldn't use an index from
// the `id` property here, I'd find it in the `values` array wherever it
// is _now_ instead (it may have moved).
values[index] = {...values[index], name};
return {values};
});
}
Note that this line in the above:
values[index] = {...values[index], name};
...uses property spread syntax added in ES2018 (and shorthand property syntax, just name instead of name: name).
I would use the Array.prototype.map function with combination of the object spread syntax (stage 4):
Note that i changed the name of the default object to obj.
default is a reserved key word in javascript
let obj = {
name: '',
age: ''
}
this.state = {
values: Array(2).fill(obj)
}
updateName(event){
const {id, value} = event.target;
this.setState(prev => {
const {values} = prev;
const nextState = values.map((o,idx) => {
if(idx !== id)
return o; // not our object, return as is
return{
...o,
name: value;
}
});
return{
values: nextState
}
});
}
There is an easy and safe way to achieve that through the following:
this.setState({
values: [ newObject, ...this.state.values],
});
this will create an instance of the state and change the value of an existing object with new object.
After the user clicks a button I want to replace the data in my vuex store with the data from my local object. But I am stuck at the mutation. Here is some code for more details.
This is the method that is called after the user clicks a button.
(this.tableview is a local object with the same values as the vuex object)
updateVuexTableview() {
// eslint-disable-next-line no-console
console.log("Update Vuex Table view");
this.updateTableview(this.tableview)
}
In this method my vuex action is called. Which looks like this:
(updTableview is the new data that I want to insert)
async updateTableview({commit}, updTableview) {
const response = await axios.put(
`http://localhost:3000/tableview`,
updTableview
);
// eslint-disable-next-line no-console
console.log(response.data);
commit('updateTableviewMut', response.data);
},
This is the mutation that is called. That's where I am stuck. I have tried to pop the data and push it again, but nothing works so far.
updateTableviewMut: (state, updTableview) => {
state.tableview.push(updTableview)
},
This right here is my state:
const state = {
tableview: {
"thema_c": true,
"status_c": true,
"priority_c": true,
"custom1_c": true,
"custom2_c": true,
"customFieldName1": "Custom1",
"customFieldName2": "Custom2"
},
};
You cannot update an object by .push you have to reassign object or set a specific field.
Just change your code like this:
If you want to add a key value pair you can do:
updateTableviewMut: (state, updTableview) => {
state.tableview['keyname'] = updTableview
}
Or if you want to reassign an object you can do:
updateTableviewMut: (state, updTableview) => {
state.tableview = updTableview
}
I have an array of objects that is saved into a userList useState which is composed of:
[{
firstName: "blah"
lastName: "blah2"
}
{
firstName: "test"
lastName: "test2"
}]
I have a useEffect that calls a function and returns a value. I want to store a new key and value to each user in userList.
useEffect(() => {
userList.forEach((user, index) =>
returnNewValueForNewKeyFunction(user, index).then(newValue => {
userList[index]['newKey'] = newValue
//this console.log shows new field and value
console.log(userList)
//this console.log ALSO shows new field and value
console.log(JSON.stringify(contactList[index]))
})
)
}
}, [])
This is fine if I'm operating out of console.log, but unfortunately I need to render the data onto the page.. in my render I have:
return (
<TableBody>
{userList
.map((user, index) => (
<TableRow>
<TableCell>
{user.newKey}
</TableCell>
)
user.newKey is showing as blank and it seems like the user wasn't updated at all. How can I make it so the value is actually updated and can be read from when rendering?
You shouldnt mutate your list, you should use useState to store your list, so something like this :
const [ state, setState] = useState(userList);
Then when you want to update, do something like this :
const listCopy = [...state];
//Logic to update your list here
listCopy[index][otherindex] = Value;
setState(listCopy)
Hope this helps
You are modifying your userList but not calling your set function on which means React won't know to re-render with the updated state.
Instead of mutating the current state, you should create a new array and then call the set function returned by useState with the updated array after making your changes.
It also looks like your returnNewValueForNewKeyFunction is a promise / async which means each of your item changes are happening async. You'll need to make these synchronous / wait for them all before updating your state to make your state change a single update for the UI.
E.g., putting these both together - if you are doing:
const [userList, setUserList] = useState();
You could do:
useEffect(() => {
// Since can't use an async func directly with useEffect -
// define an async func to handle your updates and call it within the useEffect func
const updateUsers = async () => {
// Create a new array for your updated state
const updatedUserList = [];
// Loop over your values inline so your can await results to make them sync
for (let index = 0; index < userList.length; index ++) {
const user = userList[index];
const newVal = await returnNewValueForNewKeyFunction(user, index);
// Create a shallow copy of the original value and add the newValue
updatedUserList[index] = { ...user, newKey: newValue };
// ... Any other logic you need
}
// Call set with the updated value so React knows to re-render
setUserList(updatedUserList);
};
// Trigger your async update
updateUsers();
}, [])
I have an array of 6 objects which have a uid and nothing else. This is so I can repeat over them and have some placeholder content until an object is ready to be added into the array. I set a unique key when a new object is selected. However if I select the same object twice, even though I'm setting a unique key. It seems to update the unique key on the duplicate item (even though the unique key is different).
Might be easier to see the code/app in action here, an example of the problem would be clicking squirtle then blastoise, take a note of the uid's shown. Then click squirtle again and for some reason it updates the old squirtle with the new squirtles uid causing a duplicate key error. https://codesandbox.io/s/l75m9z1xwq or see code below. Math.random is just placeholder until I can get this working correctly.
const initState = {
party: [
{ uid: 0 },
{ uid: 1 },
{ uid: 2 },
{ uid: 3 },
{ uid: 4 },
{ uid: 5 }
]
};
When I click on something this is triggered:
handleClick = pokemon => {
// setup a uid, will need a better method than math.random later
pokemon.uid = Math.random();
this.props.addToParty(pokemon);
};
This then calls a dispatch which triggers the following reducer. Which essentially just checks if the object has no normal ID then replace the content with the payload sent over. It does this but also somehow updates any previous objects with the same uid even though the if statement does not run against them.
const rootReducer = (state = initState, action) => {
if (action.type === "ADD_POKEMON") {
let foundFirstEmptyPoke = false;
const newArray = state.party.map((pokemon, index) => {
if (typeof pokemon.id === "undefined" && foundFirstEmptyPoke === false) {
foundFirstEmptyPoke = true;
pokemon = action.payload; // set the data to the first object that ios empty
}
// if we get to the last pokemon and it's not empty
if (index === 5 && foundFirstEmptyPoke === false) {
pokemon = action.payload; // replace the last pokemon with the new one
}
return pokemon;
});
return {
party: newArray
};
}
return state;
};
The problem here is that, when you click to select a pokemon, you mutate the data you retrieved from the API:
handleClick = pokemon => {
pokemon.uid = Math.random(); // HERE
this.props.addToParty(pokemon);
};
You actually mutate the react state. What you should do is clone your pokemon data object, add an uid to the clone you just generated and update your redux state with it:
handleClick = pokemon => {
this.props.addToParty({
...pokemon,
uid: Math.random()
});
};
That way, no references to the actual react state are kept. Because that was what was happening when you say it updates the old squirtle with the new squirtles uid. When you tried to add another pokemon, you updated the data you retrieved from your API which was also referenced from your first pokemon slot (from your redux state).
In react/redux it's always better to not mutate objects:
this.props.addToParty({...pokemon, uid: Math.random()});
You are mutating the state. Use spread syntax *** to copy the state before updating.
return {
...state,
party: newArray
}
Actually I need to handle mysite frontend fully with json objects(React and lodash).
I am getting the initial data via an ajax call we say,
starred[] //returns empty array from server
and am adding new json when user clicks on star buton it,
starred.push({'id':10,'starred':1});
if the user clicks again the starred should be 0
current_star=_findWhere(starred,{'id':10});
_.set(curren_star,'starred',0);
but when doing console.log
console.log(starred); //returns
[object{'id':10,'starred':0}]
but actually when it is repeated the global json is not updating,while am performing some other operations the json is like,
console.log(starred); //returns
[object{'id':10,'starred':1}]
How to update the global , i want once i changed the json, it should be changed ever.Should I get any idea of suggesting some better frameworks to handle json much easier.
Thanks before!
Working with arrays is complicated and usually messy. Creating an index with an object is usually much easier. You could try a basic state manager like the following:
// This is your "global" store. Could be in a file called store.js
// lodash/fp not necessary but it's what I always use.
// https://github.com/lodash/lodash/wiki/FP-Guide
import { flow, get, set } from 'lodash/fp'
// Most basic store creator.
function createStore() {
let state = {}
return {
get: path => get(path, state),
set: (path, value) => { state = set(path, value, state) },
}
}
// Create a new store instance. Only once per "app".
export const store = createStore()
// STARRED STATE HANDLERS
// Send it an id and get back the path where starred objects will be placed.
// Objects keyed with numbers can get confusing. Creating a string key.
const starPath = id => ['starred', `s_${id}`]
// Send it an id and fieldId and return back path where object will be placed.
const starField = (id, field) => starPath(id).concat(field)
// import to other files as needed
// Add or replace a star entry.
export const addStar = item => store.set(starPath(item.id), item)
// Get a star entry by id.
export const getStar = flow(starPath, store.get)
// Get all stars. Could wrap in _.values() if you want an array returned.
export const getStars = () => store.get('starred')
// Unstar by id. Sets 'starred' field to 0.
export const unStar = id => store.set(starField(id, 'starred'), 0)
// This could be in a different file.
// import { addStar, getStar, getStars } from './store'
console.log('all stars before any entries added:', getStars()) // => undefined
const newItem = { id: 10, starred: 1 }
addStar(newItem)
const star10a = getStar(10)
console.log('return newItem:', newItem === star10a) // => exact match true
console.log('star 10 after unstar:', star10a) // => { id: 10, starred: 1 }
console.log('all stars after new:', getStars())
// Each request of getStar(10) will return same object until it is edited.
const star10b = getStar(10)
console.log('return same object:', star10a === star10b) // => exact match true
console.log('return same object:', newItem === star10b) // => exact match true
unStar(10)
const star10c = getStar(10)
console.log('new object after mutate:', newItem !== star10c) // => no match true
console.log('star 10 after unstar:', getStar(10)) // => { id: 10, starred: 0 }
console.log('all stars after unstar:', getStars())
I think the problem is in mutating original state.
Instead of making push, you need to do the following f.e.:
var state = {
starred: []
};
//perform push
var newItem = {id:10, starred:1};
state.starred = state.starred.concat(newItem);
console.log(state.starred);
//{ id: 10, starred: 1 }]
var newStarred = _.extend({}, state.starred);
var curr = _.findWhere(newStarred, {id: 10});
curr.starred = 0;
state = _.extend({}, state, {starred: newStarred});
console.log(state.starred)
//{ id: 10, starred: 0 }]
To solve this in a more nice looking fashion, you need to use either React's immutability helper, or ES6 stuff, like: {...state, {starred: []}} instead of extending new object every time. Or just use react-redux =)