please doee anyone have an idea how the first code works and the second doesn't? i saw the first code somewhere and i have been trying to understand the idea behind it. thanks
const loadable = (importFunc, { fallback = null } = { fallback: null }) => {
const LazyComponent = lazy(importFunc);
return props => (
<Suspense fallback={fallback}>
<LazyComponent {...props} />
</Suspense>
);
};
When you destructure in the argument list with an object, an identifier after a colon indicates the new variable to put the property into.
This:
const test2 = ({ fallback: foo }) => {
// rest of function
means
const test2 = (parameter) => {
let foo = parameter.fallback;
// rest of function
But you're not using foo, but null - which the interpreter (and specification) recognizes as almost certainly being an accidental error. You don't want to put a value into an identifier named null, so it prohibits you from doing so.
Neither of the codes you provided are valid syntax for this reason.
If you just want to put the fallback property of the argument into a variable, omit the colon.
const test2 = ({ fallback }) => {
console.log(fallback);
}
//calling
test2({fallback: "hello"});
If you also want to provide a default value in case the property is undefined, use = (not :).
const test2 = ({ fallback = null }) => {
console.log(fallback);
}
//calling
test2({fallback: "hello"});
test2({});
If you also want to provide a default value for the whole object argument in case no arguments are passed, use another = after the argument is listed (very similar to what you're doing originally, but, again, with =, not :)
const test2 = ({ fallback = null } = {}) => {
console.log(fallback);
}
//calling
test2({fallback: "hello"});
test2({});
test2();
Related
i am having an hard time understanding the usability of passing an object as a parameter of a function.
I am trying to learn to use Next.js and they usually have this way of interacting with their code.
Can someone explain me in short words the usability of it ? I have an example listed below.
Thank you !
function Page({ data }) {
// Render data...
}
Understanding the usability of passing an object as an argument of a function.
Explained me with an example the usability of it.
This syntax is called Destructuring assignment.
So { data } in function Page({ data }){ ... is not an object definition, but a destructuring of the object passed as the first argument of the function,
which results in a variable data available in the function body.
More details ...
Preface
I assume you are familiar with basic destructuring assignments, which look like e.g.:
const person = { name: 'Alice', age: 20 }; // <-- define object
const { name, age } = person; // <-- destructure properties from the object
But note that you not necessarily need to bind this object to a variable name to be able to destructure it:
const { name, age } = { name: 'Alice', age: 20 }; // <-- define object and destructure it right away, without a variable name
Destructuring of arguments in React components
In a React function-component you can pass properties and access them inside the component like this:
function Page( props ) {
return <div>
Data: { props.data }
</div>;
};
function App() {
return <Page data="Hello" />;
};
You can also destructure the props at the beginning of the function, so that you don't have to type props.... for each property:
function Page( props ) {
const { data } = props;
return <div>
Data: { data }
</div>;
};
React components always have the same arguments, so you basically always have the props argument. Here "props" is a variable name that is bound to the props object (which contains all the properties passed to the component).
But you almost never actually need the props object itself, you likely only want the properties inside it.
So you don't really need to bind the object with the properties to the name "props", only to destructure it right away in the next line,
which makes the props object obsolete for the rest of the function body.
For this, javascript has a "shortcut syntax" to destructure an argument directly in the function arguments section, without specifying a name for the argument:
function Page({ data }){
return <div>
Data: { data }
</div>;
};
This is common syntax. I wouldn't say that it is better or worse than other options.
The answer was great and one huge positive benefit of an object prop is you dont have to pass parameters in a specific order, which is super helpful when function argument list becomes long.
This is called destructing. Works for objects and arrays:
const obj = { x: 'hello', y: 'world', z: 'whats up' };
const { x } = obj;
console.log('obj.x is', obj.x);
console.log('x is', x);
const { y, z: myNewName } = obj;
console.log('obj.y is', obj.y);
console.log('y is', y);
console.log('obj.z is', obj.z);
console.log('myNewName is', myNewName);
// console.log(z) will error
const arr = ['value', v => console.log(v)];
const [whateverIWantToCallIt, namesDoNotMatterJustTheIndex] = arr;
console.log('accessing from deconstruction of arr')
console.log(whateverIWantToCallIt);
console.log(namesDoNotMatterJustTheIndex('whats up'));
console.log('accessing from arr')
console.log(arr[0]);
console.log(arr[1]('whats up'));
I have a js object in which I return my endpoint addresses from api. This is a very nice solution for me, it looks like this:
export const API_BASE_URL = 'http://localhost:3000';
export const USERS = '/Users';
export default {
users: {
checkEmail: (email) => `${API_BASE_URL}${USERS}/${email}/checkEmail`,
notifications: `${API_BASE_URL}${USERS}/notifications`,
messages: `${API_BASE_URL}${USERS}/messages`,
},
};
Now I can call this address in my redux-saga to execute the xhr query:
import { api } from 'utils';
const requestURL = api.users.notifications;
But I'm a bit stuck because now I have a problem - base path is missing here: '/users'.
Now when I call api.users, then I get a object. I would like to have a default value after calling the object like:
import { api } from 'utils';
const requestURL = api.users; // http://localhost:3000/Users
const requestURL2 = api.users.notifications; // http://localhost:3000/Users/notifications
I know that I could add a new string with the name 'base' to the object and add '/Users' there, but I don't like this solution and I think, there is a better solution.
You could do one of the following:
extend the String class
const API_BASE_URL = "http://localhost:3000"
const USERS = "/Users"
class UsersEndpoints extends String {
constructor(base) {
super(base)
}
// this is still a proposal at stage 3 to declare instance variables like this
// if u want a truly es6 way you can move them to the constructor
checkEmail = (email) => `${API_BASE_URL}${USERS}/${email}/checkEmail`
notifications = `${API_BASE_URL}${USERS}/notifications`
messages = `${API_BASE_URL}${USERS}/messages`
}
// you can use userEndpoints itself as a string everywhere a string is expected
const userEndpoints = new UsersEndpoints(API_BASE_URL)
export default {
users: userEndpoints
}
The previous is just actually equivalent to
...
const userEndpoints = new String(API_BASE_URL)
userEndpoints.notifications = `${API_BASE_URL}${USERS}/notifications`
...
Obviously this is not recommended: you should not extend native classes, there are many disadvantages to this approach.
An obvious example is that there could be a conflict between the properties you use and the properties that might be brought by the native class
override the toString method
...
export default {
users: {
checkEmail: (email) => `${API_BASE_URL}${USERS}/${email}/checkEmail`,
notifications: `${API_BASE_URL}${USERS}/notifications`,
messages: `${API_BASE_URL}${USERS}/messages`,
toString: () => API_BASE_URL
},
};
// this is actually not much different than the previous method, since a String is an objet with an overridden toString method.
// That said this method is also not recommended since toString is used in many places in native code, and overriding it just to substitute a string value will make information get lost in such places, error stacks for example
Achieve what u want using the language features intended for such a use case
What you are asking is to make the same variable to have different values in the same time, which is not possible in the language syntax, and it makes sense because it makes it hard to reason about code.
that being said i recommend something of the following nature
// it is also better to use named exports
export const getUsersEndpoint = ({
path = "",
dynamicEndpointPayload = {},
} = {}) => {
switch (path) {
case "notifications":
return `${API_BASE_URL}${USERS}/notifications`
case "messages":
return `${API_BASE_URL}${USERS}/messages`
case "checkEmail":
return `${API_BASE_URL}${USERS}/${dynamicEndpointPayload.email}/checkEmail`
// you still can do checkEmail like this, but the previous is more consistent
// case "checkEmail":
// return (email) => `${API_BASE_URL}${USERS}/${email}/checkEmail`
default:
return `${API_BASE_URL}`
}
}
// you can use it like this
getUsersEndpoint() // returns the base
getUsersEndpoint({path: 'notifications'})
You can extend prototype to achieve this behaviour:
export const API_BASE_URL = 'http://localhost:3000';
export const USERS = '/Users';
const users = `${API_BASE_URL}${USERS}`
const baseUrls = {
checkEmail: (email) => `${users}/${email}/checkEmail`,
notifications: `${users}/notifications`,
messages: `${users}/messages`,
}
Object.setPrototypeOf(users.__proto__, baseUrls);
export default {
users
};
Try having object will all user endpoint and a function that return a value of a end point
const user = {
default: '/users',
notification: '/notification',
profile: '/profile',
getEndPoint(prop) {
if(this[prop] === 'default' ){
return this[prop];
} else {
if(this[prop]) {
return this.default + this[prop];
}
}
}
}
So you can have more end points that come under user and you can simply call
const requestURL = api.user.getEndPoint('default'); // http://localhost:3000/Users
const requestURL2 = api.user.getEndPoint('notifications'); // http://localhost:3000/Users/notification
class CompTable extends React.Component{
constructor(props){
super(props);
this.state = {
products: [],
attributes: [],
attDesc: [],
};
this.getEntries = this.getEntries.bind(this);
}
getEntries = async () => {
const response = await fetch('/api/hello/data');
const body = response.json();
return body;
};
componentDidMount(){
this.getEntries()
.then((resolve) => this.setState({
products: resolve.products,
attributes: resolve.attributes,
attDesc: resolve.attributesDescription}))
.catch(err=>console.log(err));
};
render(){
let obj = this.state.products[1].attributes;
console.log(obj);
return(
<div id = "comp">
<CompHeading comp={this.state.products}/>
</div>
);
}
}
export default CompTable;
The line let obj = this.state.products.attributes returns the mentioned error. What's bizzare is that if I remove the ".attributes", the console logs the product object, with the "attributes" property inside it. It seemed like the object just disappeared when I try to access its property XD. Anyone knows the reason why?
Another strange thing is when i remove the ".attributes" the console of my browser logs six objects (though i only call console.log once) - 4 of the objects show undefined while the two are the correct product[1] object.
Don't trust a console.log, it's not accurate. its value can change. To fix that you can use JSON.stringify on the object you are logging to show its real value when you are logging it.
For your problem, you could do something like that :
if (!this.state.products[1]?.attributes) return <Loader />
else
return (... your content...);
This way you will render a Loader while you data are not available.
You are trying to access this.state.products[1].attributes even before it is populated.
It gets populated from componentDidMount which will run after render function is done execution.
We need to change the render function variable declaration to this -:
let obj = this.state.products.length > 0 && this.state.products[1].attributes;
console.log(obj);
So that way it will get value as undefined in the first pass. And in the second pass when setState gets called from componentDidMount it will get the correct value and your code won't break midway.
I am using typescript and compiling to ES2016.
I noticed that I am calling two different functions with similar this object in a Function.prototype.call().
I tried to merge both this objects by using a common object which would use ...spread in the beginning of the object like so
let selfShared = {
props,
// ...
};
let selfHost = {
...selfShared,
// ...
};
let selfGuest = {
...selfShared,
// ...
};
The idea of using the spread in the begging was that I could overwrite the shared properties in either of the this objects if I saw it fit.
But unlike when setting props straight in the this objects using the spread gave out weird results, which turned out to be because tsc compiled the code as
let selfShared = {
props
};
let selfHost = Object.assign(Object.assign({}, selfShared), {
// ...
});
// ...
using my code
let state = undefined;
let attributes = {};
let selfShared = {
props: attributes
};
let selfHost = {
...selfHost,
get state() {
console.log("selfHost get state");
return state;
},
set state(originalStates) {
console.log("selfHost set state");
!state ? state = originalStates : console.error("`this.states` already defined in the host function.");
}
}
the output looks like
let state = undefined;
let attributes = {};
let selfShared = {
props: attributes
};
let selfHost = Object.assign(
Object.assign({}, selfShared), {
get state() {
console.log("selfHost get state");
return state;
},
set state(originalStates) {
console.log("selfHost set state");
!state ? state = originalStates : console.error("`this.states` already defined in the host function.");
}
});
now at least on firefox 74 to 77 inserting both of the codes into the console and adding
// ...
selfHost.state = {
thing: "some"
};
selfHost.state = {
some: "thing"
};
throws out different logs...
The precompiled code gives me two of set state and an error which are the expected outputs, but the compiled code gives me a get state and ignores the rule in set state outputting
{
some: "thing"
}
instead of the expected
{
thing: "some"
}
as in the precompiled code?
Setting the spread into the bottom of the file compiles to
let selfHost = Object.assign({
get state() {
console.log("selfHost get state");
return state;
},
set state(originalStates) {
console.log("selfHost set state");
!state ? state = originalStates : console.error("`this.states` already defined in the host function.");
}
}, selfShared);
which gives the right output but doesn't allow me to overwrite the properties given by selfShared.
Can you explain why this happens with Object.assign and if there is a trick to get an output from tsc that still lets me do what I originally wanted?
When using spread
let obj = {
...otherObj,
// ...
}
or
let obj = Object.assign({}, otherObj, {
// ...
})
The spread is interpreted as literal which means that as in the polyfill the properties are read as strict values, which means that normal values are read normally, setters are ignored and getters are read as normal values.
Setters and getters work as written in the question when the spread is written in the end as
let obj = {
// ...
...otherObj
}
or
let obj = Object.assign({
// ...
}, otherObj)
since otherObj only extends the unique objects.
I have this code in a friend of mine React application and I need to understand what this code does explicitly:
const Component = ()=> (
<QueryFetcher>
{({ data }) => {
const { user: { profile = {} } = {} } = data
return (
<div>
{profile.username && profile.username}
</div>
)
}}
</QueryFetcher>
)
What is this line for?
const { user: { profile = {} } = {} } = data
Is it correct to assign something to {} using { user: { profile = {} } = {} } in this functional component? Or in a render() hook of a stateful component in React?
const { user: { profile = {} } = {} } = data basically means that your retrieving the user profile.
const means that you are creating a new variable
{ user: { profile } } } means that you are retrieving profile inside of user
= {} means that if the object is undefined, use an empty object so it will not fail because doing user.profile will throw an error if user is undefined.
= data means that you retrieving this info from the data variable
So, this line means, from the variable data, go take the user, if the user is undefined, use an empty object. Then, go take the profile, if the profile is undefined, use an empty object. Then create a variable called profile with the result. This is like doing this:
const user = data.user === undefined ? {} : data.user;
const profile = user.profile === undefined ? {} : user.profile;
What is this line for?
const { user: { profile = {} } = {} } = data
It's basically just chained ES6 object-destructuring with default values.
What this line does in words:
Read "user" from "data", if "user" is undefined, assign {} as a default value
Read "profile" from "user", if "profile" is undefined, assign {} as a default value
Is it correct
It is mostly a short-hand syntax used to remove repetitive stuff. So instead of accessing multiple object props separately e.g.
this.props.prop1, this.props.prop2, ...
you can use
const { prop1, prop2 } = this.props;
It also helps other readers later quickly understanding what variables are used in a method if all necessary props are destructured at the start.