React.Element object internals are not shown when serialized - javascript

I can have a functional component like this:
const FuncList = ({ name }) => {
return (
<div className="shopping-list">
<h1>Shopping List for {name}</h1>
<ul>
<li>Instagram</li>
<li>WhatsApp</li>
<li>Oculus</li>
</ul>
</div>
)
}
I can instantiate it to see basically the entire element content:
const obj = FuncList({ name: 'Pete' })
console.log(JSON.stringify(obj, null, 4))
-----
{
'type': 'div',
'key': null,
'ref': null,
'props': {
'className': 'shopping-list',
'children': [
{
'type': 'h1',
'key': null,
'ref': null,
'props': {
'children': [
'Shopping List for ',
'mike'
]
},
'_owner': null,
'_store': {}
},
{
'type': 'ul',
'key': null,
'ref': null,
'props': {
'children': [
{
'type': 'li',
'key': null,
'ref': null,
'props': {
'children': 'Instagram'
},
'_owner': null,
'_store': {}
},
....
]
},
'_owner': null,
'_store': {}
}
]
},
'_owner': null,
'_store': {}
}
Now when I use it with JSX syntax this:
const obj = <JSXList name="Pete" />;
// same as: const obj = React.createElement(JSXList, { name: "Pete" });
console.log(JSON.stringify(obj, null, 4))
I get almost empty object. Where is the link to JSXList stored? Where are all the properties? Are they hidden inside the object somehow? It looks like 'return' method of JSXList was never called, thus it was never expanded, but why there's no reference to it?
{
'key': null,
'ref': null,
'props': {
'name': 'Pete'
},
'_owner': null,
'_store': {}
}
If I would use 'div' instead of JSXLint as a first argument, I would get at least 'type' property which would indicate the purpose of the element. Is there any design rationale behind that?
const obj = React.createElement('div', { name: "Pete" });
console.log(JSON.stringify(obj, null, 4))
Is there any point in hiding that reference? It sort of obscure object introspection, as far as I can tell.
You can use codepad to play around if you like.

I get almost empty object. Where is the link to JSXList stored?
In obj.type. You're not seeing type in the log because functions are not serializable, so JSON.stringify skips them.
Where are all the properties?
It's there. You only gave it one property: name: 'pete'. If you're expecting to see divs and such, the reason you're not is that the following two lines are not equivalent:
const obj = FuncList({ name: 'Pete' })
const obj = <FuncList name="Pete" />;
As you pointed out in a comment in your code, the jsx expands to this, which again is not equivalent to calling FuncList:
React.createElement(FuncList, { name: "Pete" });
React.createElement creates a small object describing what to render, which is the small object you're seeing. What normally happens next is that react's reconciliation algorithm decides whether it needs to take things farther. If it does, then only then will it instantiate the FuncList, calling FuncList({ name: 'Pete' }) and producing the larger object.

Related

How to map JSON Object to array with keys in javascript

There's about million questions (and answers) out there on this topic, but none of them are doing what I need to do. I have a JSON object where the value for each key is an object. I want to convert this to an array and maintain the top level keys.
{
"someKey1": {
active: true,
name: "foo"
},
"someKey2": {
active: false,
name: "bar"
}
}
If I use Object.keys() I get the top level keys, but not their values. If I use Object.values() I get an array with all the values, but not their keys. I'm trying to use keys and map, but am only getting the values returned:
const data = {
"someKey1": {
active: true,
name: "foo"
},
"someKey2": {
active: false,
name: "bar"
}
}
const items = Object.keys(data).map(function(key) {
return data[key];
});
// returns [{active: true, name: foo},{active: false, name: bar}]
Is there a way to get both? I want to get an array I can iterate over that looks something like this:
[{
key: someKey1,
active: true,
name: "foo"
},
{
key: someKey2,
active: true,
name: "foo"
}]
OR
[
"someKey1": {
active: true,
name: "foo"
},
"someKey2": {
active: false,
name: "bar"
}
]
I think you are going in the right direction, if you want to add the "key" property you have to map the properties manually and for the second option since you don't need the "key" property it can be done a little bit more elegantly:
For the first option:
Object.keys(data).map(v => ({
key: v,
...data[v]
}));
For the second option even simpler:
Object.keys(data).map(v => ({[v]: {...data[v]}}))
You can easily map your data to a new object:
Object.keys(data).map(key => ({ ...data[key], "key": key }));

How to declare value in key:value pair as null if doesn't exist

I have an object like this:
{
customer: newCustomer.id,
coupon: customer.coupon,
items: [
{
plan: customer.plan
},
]
}
customer.coupon might not always exist, and when it doesn't exist I need it to be null. It can't just be an empty string because that throws an error. I tried coupon: customer.coupon || null but that didn't work.
It's not the cleanest solution, but this works as well:
if (customer.coupon == undefined) {
var nullObject = {
customer: newCustomer.id,
coupon: null,
items: [
{
plan: customer.plan
},
]
}
return nullObject;
} else {
var nonNullObject = {
customer: newCustomer.id,
coupon: customer.coupon,
items: [
{
plan: customer.plan
},
]
}
return nonNullObject;
}
Try this:
{
customer: newCustomer.id,
coupon: customer.coupon || null,
items: [
{
plan: customer.plan
},
]
}
JavaScript has the cool ability to take the last value if all previous values are falsy (That include false, 0, null and others)
So this code:
customer.coupon || null
Will use customer.coupon, but it that value is falsy the it will take the null.
UPDATE
I just saw that you stated that this isn't working, what error are you getting?
let newCustomer = {"id":1};
let customer = {"coupon":null,"plan":1};
var obj = {
customer: newCustomer.id,
coupon: customer.coupon,
items: [
{
plan: customer.plan
},
]
}
console.log(obj);
if(obj.coupon==null)
delete obj["coupon"]
console.log(obj);
and the result from node console I got is

How to modify a specific element of a nested object using ES6?

I have this initialStore:
const initialState =
{
0:{
screen_name: '1',
props: null,
},
1:{
screen_name: '2',
props: null,
},
2:{
screen_name: '3',
props: null,
},
3:{
screen_name: '4',
props: null,
},
4:{
screen_name: '5',
props: null,
},
}
I want to know how can I modify for example the value state.0.screen_name: 1, while keeping the rest of the original state using ES6?
This is the approach that I have so far:
export const navigationReducer = createReducer(initialState, {
[types.CHANGE_SCREEN_EXPERIMENTAL](state,action){
return{...state[action.id], screen_name:action.screen_name, ...state
};
}
},
);
However, that returns this:
The action.id is 0, it should modify the state[0] element, however, it copied its elements, modified them and placed them on the state object itself instead of the state[0] object.
Desired result:
navigationReducer:{
0: {
props: null,
screen_name: "ad"
}
1:Object
2:Object
3:Object
4:Object
}
I do not want the screen_name nor props to be outside those objects (0,1,2,3,4).
Any ideas are well appreciated.
I found the way to do it using ES6
Here is how:
export const navigationReducer = createReducer(initialState, {
[types.CHANGE_SCREEN_EXPERIMENTAL](state,action){
return{
...state,[action.id]:{
...state[action.id],
screen_name:action.screen_name,
props:action.props,
}
};
}
},
);
This results in:
Thank you very much for your help and time #ibrahimmahrir.

ImmutableJS - update value in a List

I have a Map like this (in ImmutableJS):
{arrayOfValues: [
{one: {inside: 'first in array'}},
{one: {inside: 'second in array'}}
]}
And I want to update the value "inside" in the second entry in the "arrayOfValues" array. How can I do it? This is what I have now and it says "Uncaught Error: invalid keyPath"
theMap.update('arrayOfValues',(list)=>{
return list.setIn([1,'one','inside'],'updated value');
})
I also tried directly this and it didn't work:
theMap.setIn(['arrayOfValues',1,'one','inside'],'updated value');
After several hours of looking for the solution, I appreciate any help. Thank you.
What you are doing is correct (see this JSBin).
const orig = Immutable.fromJS({
arrayOfValues: [
{ one: { inside: 'first in array' } },
{ one: { inside: 'second in array' } },
]
});
const updated = orig.setIn(['arrayOfValues', 1, 'one', 'inside'], 'updated value');
console.log(updated.toJS());
// {
// arrayOfValues: [
// { one: { inside: 'first in array' } },
// { one: { inside: 'second in array' } },
// ]
// }
When you call orig.setIn(), it doesn't modify orig directly. That's the whole purpose of this Immutable library. It doesn't mutate the existing data but creates a new one from the existing one.
Your setIn example works as you should see in this plunkr:
http://plnkr.co/edit/1uXTWtKlykeuU6vB3xVO?p=preview
Perhaps you are assuming the value of theMap will be changed as a result of the setIn?
As these structures are immutable, you must capture the modified value in a new variable as var theMap2 = theMap.setIn(['arrayOfValues',1,'one','inside'],'updated value');
activePane is the index of Object in Array(List) that I had to modify
case CHANGE_SERVICE:
var obj = {
title: '1212121 Tab',
service: '',
tagName: '',
preDefinedApi: '',
methodType: '',
url: '',
urlParams: [{
label: '',
name: '',
value: '',
}],
headers: [{
label: '',
name: '',
value: '',
}],
};
var activePane = state.get('activePane');
var panes = state.setIn(['panes', activePane, 'service'], action.val);
return state.setIn(['panes', activePane, 'service'], action.val);

dijit.tree with empty folders

i have created a tree select that shows a dijit.tree in the dropdown. Now I do not want the user to select a folder even if it is empty. User should only be able to select the end nodes or leaves. dijit.tree treats all empty folders as leafs. how do I get that sorted?
You need to override the _onClick or setSelected methods. This gets complicated if you use the multi-parental model ForestStoreModel.
See fiddle.net
Try doing as such, this will only work for select multiple false:
getIconClass: function fileIconClass(item, nodeExpanded) {
var store = item._S,
get = function() {
return store.getValue(item, arguments[0]);
};
// scope: dijit.Tree
if (item.root || get("isDir")) {
if (!item || this.model.mayHaveChildren(item) || get("isDir")) {
return (nodeExpanded ? "dijitFolderOpened" : "dijitFolderClosed");
} else {
return "dijitLeaf";
}
} else {
return "dijitLeaf";
}
},
onClick: function(item, treeNode, e) {
var store = item._S,
get = function() {
return store.getValue(item, arguments[0]);
};
if (get("isDir")) this.set("selectedItems", []);
}
Adapt as you see fit, matching your json data - in particular the isDir, the above works on a sample of json like this
{
identifier: 'id',
label: 'foo',
items: [
{
id: 'item1',
foo: 'file1',
isDir: false},
{
id: 'item2',
foo: 'emptyDir',
isDir: true},
{
id: 'item3',
foo: 'dir',
isDir: true,
children: [
{
id: 'item3_1',
foo: 'fileInDir',
isDir: false}
]}
]
}

Categories