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 =)
Related
I receive JSON data from the service, but the keys change in the data with each request, below I will give an example in three versions.
Exmaple 1:
{
"trackingPayloads": {
"Rltyn4gLRIWRKj9kS0YpWXytG81GZwcPWjEE7f31ALlq": "{"title":"Red Shoes","index":3,"id":"17777","type":"category"}',
"ywtA6OyM0hzVZZvnUjxoxJDI1Er9ArfNr8XKyi1D5Zzk": "{"title":"White Shoes","index":3,"id":"17777","type":"category"}',
}
}
Example 2:
{
"trackingPayloads": {
"36tW7DqZ3H9KKBEAumZmowmUwmDRmVCjQgv5zi9GM3Kz": "{"title":"Red Shoes","index":3,"id":"17777","type":"category"}',
"OgtE51n3YtvrVXWLFjPmpnRt2k5DExF7ovxmBTZrZ6wV": "{"title":"White Shoes","index":3,"id":"17777","type":"category"}',
}
}
Example 3:
{
"trackingPayloads": {
"k2toY29glt2JEp9Wi1X5M7ocno0E0mS4JQVyDuGyQ2rM": "{"title":"Red Shoes","index":3,"id":"17777","type":"category"}'",
"5ef2ec3c3573eebecc9690b69619ec7b9c93b609": "{"title":"White Shoes","index":3,"id":"17777","type":"category"}',
}
}
As you can see, the data included in the keys does not change since I am requesting the same information, but the key will change with each request.
Please help, what are the options to get the data Title, Index and any other content in these keys using node js?
Only one option came to my mind - to rename the keys upon receipt in 1,2,3 ... and then get data from them, but this needs to be done dynamically, since about 120 requests per minute are made, you need to get this data quickly, there are no options to save it to a file (I didn’t understand how)
UPDATE, added my code.
I am attaching an example of my code, the idea is to eventually get the data I need from the right keys from trackingPayloads, please help with the code <3
const AwaitAPIResponse = await ProductAPI(product_sku);
const $ = cheerio.load(AwaitAPIResponse);
const JSONDATA = [];
$('pre').each(function() {
JSONDATA.push($(this).text());
});
const ProductJson = JSON.parse(JSONDATA[0]) // this is where I get all the data
const MainJson = ProductJson["trackingPayloads"] // here I go to the trackingPayloads you saw above
How can I get the data I need?
You can use Object.keys() to get all the different keys of an object and use a loop to go through them.
Therefore, you can rework this code in such a way that each of the values is stored as an element in an array, maybe makes the data easier to work with:
const convert = object => {
const ret = []
for (const key of Object.keys(object)) {
ret.push(object[key])
}
return ret
}
This will give you following result for your use case:
[{"title":"Red Shoes","index":3,"id":"17777","type":"category"},
{"title":"Red Shoes","index":3,"id":"17777","type":"category"}]
The way you'd call this is as follows:
const some_parsed_json = {
"k2toY29glt2JEp9Wi1X5M7ocno0E0mS4JQVyDuGyQ2rM": {
title:"Red Shoes",
index:3,
id:"17777",
type:"category"
},
"5ef2ec3c3573eebecc9690b69619ec7b9c93b609": {
title:"Red Shoes",
index:3,
id:"17777",
type:"category"
}
}
const json_object_values = convertor(some_parsed_json)
If you don't car about the key you could use Object.values on the received object to get the values
Object.values(payload)
// With your example it will return:
// [{"title":"Red Shoes","index":3,"id":"17777","type":"category"},
// {"title":"Red Shoes","index":3,"id":"17777","type":"category"}]
or in a more complete example
async function getParsedValues() {
const responseString = await service.getData(); // '{"trackingPayloads":{"Rltyn4gLRIWRKj9kS0YpWXytG81GZwcPWjEE7f31ALlq":{"title":"Red Shoes","index":3,"id":"17777","type":"category"},"ywtA6OyM0hzVZZvnUjxoxJDI1Er9ArfNr8XKyi1D5Zzk":{"title":"White Shoes","index":3,"id":"17777","type":"category"}}}'
const parsedResponse = JSON.parse(responseString); // { trackingPayloads: { Rltyn4gLRIWRKj9kS0YpWXytG81GZwcPWjEE7f31ALlq: { title:'RedShoes', index: 3, id: '17777', type: 'category' }, ywtA6OyM0hzVZZvnUjxoxJDI1Er9ArfNr8XKyi1D5Zzk:{title:'WhiteShoes', index: 3, id: '17777', type: 'category' } }}
const values = Object.values(parsedResponse); // [{"title":"Red Shoes","index":3,"id":"17777","type":"category"}, {title:'WhiteShoes', index: 3, id: '17777', type: 'category' }]
return values;
}
I am using Redux in my React application. However items are always stored by index, like that =>
I want to store them by ids, like instead of 0 first item's index should be 41. How can I do that?
reducer.js
export function ratedPosts(state=[], action) {
enableES5()
return (
produce(state, draft => {
const rate = action.Rate
switch (action.type) {
case RATE_POST:
draft.unshift({postId: action.postId, rate: rate})
break
case RATE_POST_UPDATE:
draft.map(post => post.postId === action.postId).rate = rate
break
default:
return draft
}
})
)
}
You can't do that with arrays, but you can do that with objects. I see also that you are using Array.unshift to add new posts, keep in mind that arrays do not guarantee the sequence of the items, even though it works most of the time.
You'll need to convert your data structure to use objects instead of array, but in the getter function you could convert to an array so it can be more easily used in the frontend.
You can set an object ID programmatically using [ ]
let myObject = {}
const idOne = 'abc'
const idTwo = 'def'
draft[idOne] = "Hello" // draft.abc === "Hello"
draft[idTwo] = "World" // draft.def === "World"
draft === {
abc: "Hello",
def: "World"
}
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
}
I am trying clone an "original" node's data (as soon as I create the data) to a path that is based on the original node's path.
This is my data structure:
root: {
doors: {
111111111111: {
MACaddress: "111111111111",
inRoom: "-LBMH_8KHf_N9CvLqhzU", // I will need this value for the clone's path
ins: {
// I am creating several "key: pair"s here, something like:
1525104151100: true,
1525104151183: true,
}
}
},
rooms: {
-LBMH_8KHf_N9CvLqhzU: {
ins: {
// I want the function to clone the same data here:
1525104151100: true,
1525104151183: true,
}
}
}
My cloud function is now like this:
exports.updateRoom = functions.database.ref('/doors/{MACaddress}/ins').onWrite((change, context) => {
const beforeData = change.before.val(); // data before the write
const afterData = change.after.val(); // data after the write
const roomPushKey = change.before.ref.parent.child('/inRoom');
console.log(roomPushKey); // this is retrieving all the info about the ref "inRoom" but not its value...
Question 1) How can I get to this ref/node's value?
My code goes on by trying to get the value like this.
roomPushKey.once('child_added').then(function(dataSnapshot) {
let snapVal = dataSnapshot.val();
console.log(snapVal);
});
Question 2 (which I think is basically question 1 rephrased): How can I get the snapVal outside the then. method's scope?
return change.after.ref.parent.parent.parent.child('/rooms')
.child(snapVal).child('/ins').set(afterData);
// snapVal should go above
});
Error message: ReferenceError: snapVal is not defined
The following should work.
const admin = require("firebase-admin");
....
....
exports.updateRoom = functions.database.ref('/doors/{MACaddress}').onWrite((change, context) => {
const afterData = change.after.val(); // data after the write
const roomPushKey = afterData.inRoom;
const ins = afterData.ins;
const updates = {};
updates['/rooms/' + roomPushKey] = ins;
return admin.database().ref().update(updates);
}).catch(error => {
console.log(error);
//+ other rerror treatment if necessary
});
Here are some explanations:
You get the roomPushKey by reading the "data after the write" as an object: roomPushKey = afterData.inRoom. You don't need to do roomPushKey.once('child_added').then()
Once you have the roomPushKey, you create a new child node in the rooms node by using update() and creating an object with square brackets notation which allow you to assign the id of the node (i.e. roomPushKey).
Note that you could also do:
return admin.database().ref('/rooms/' + roomPushKey).set(ins);
Note also that you have to import firebase-admin in order to be able to do return admin.database().ref()...
Finally, I would suggest that you have a look at the three following videos from the Firebase team: youtube.com/watch?v=7IkUgCLr5oA&t=517s & youtube.com/watch?v=652XeeKNHSk&t=27s & youtube.com/watch?v=d9GrysWH1Lc. A must for anyone starting coding for Cloud Functions.