React supports both class and function components. All React files have an import React from ‘react’ statement at the top. When creating a class component (like so: class MyClassComponent extends React.Component ...), there is an explicit mention of which class this class instance extends from. However, when declaring a function component (const MyFunctionComponent = () => ()), it isn’t being explicitly said “hey this is a React component by the way.”
For all we know, this could be a function in plain JavaScript. How then, does React recognize functions as function components?
Here is the code for the isValidElement() function. In a nutshell, they check if it is an object (functions are objects) with a $$typeof of the REACT_ELEMENT_TYPE Symbol.
Beyond that though, it doesn't really matter if it is a normal function or not. For the most part, even if I make some random function, as long as it has a render() function, it'd be usable as a React element. I'm not sure if they have checks for missing lifecycle functions or not, but if they do, then it would work (if they don't, it'd throw errors).
When it comes to JavaScript "inheritence", it always just boils down to if the shape of the object matches the expected shape.
It doesn't do that, every function is valid, if it returns a valid type. However, React doesn't check statically if a function will return something valid, so it just runs it, if it's a function. In newer React versions the return value can be nearly everything. Arrays work now, strings also. Currently React does support the following types as return values:
React elements. Typically created via JSX. An element can either be a representation of a native DOM component (), or a user-defined composite component ().
String and numbers. These are rendered as text nodes in the DOM.
Portals. Created with ReactDOM.createPortal.
null. Renders nothing.
Booleans. Render nothing. (Mostly exists to support return test && pattern, where test is boolean.)
Fragment (array of valid elements)
So, unless you don't return undefined, it should work, but as said, it will execute it always if it is a function.
But it IS a plain javascript function. It simply return a value which is then interpreted by React.
Edit: To help you understand, you need to keep in mind that in JavaScript EVERYTHING (except of course primitive types) are objects (Classes, Functions, Arrays, etc) are at the end all the same.
Related
If I have a hook, e.g:
const useGetSet = (label: string) => {
const [get, set] = useState(label);
return { get, set };
};
I can map it over an array, e.g:
const labels = ['one', 'two'].map(useGetSet);
But if I expand this to a lambda, e.g:
const labels = ['one', 'two'].map((l) => useGetSet(l))
Then it causes:
React Hook "useGetSet" cannot be called inside a callback.
React Hooks must be called in a React function component or a custom React Hook
function.
(react-hooks/rules-of-hooks) eslint
Why this difference, shouldn't they be equivalent?
Additionally, if this is a violation of the rule of hooks, how should this be done?
Full working example here.
It's bad in both cases, but ESLint isn't picking up the first case.
You can see this by changing the labels array at run-time. If you increase or decrease the length it blows up (because an unexpected number of hooks was called, as per the exception), if you don't it doesn't re-render as expected. See here for an updated version of your demo.
(The reason that the rule exists is because under the hood, hooks need to be called in the same order every time. Obviously, if the number of hooks varies between renders then the order changes.)
In your specific case, because your array is fixed length and not changing then your code will work fine (no matter what the linter says). However, it's still a dangerous pattern and best avoided.
In terms of how to handle it better, if you need more than basic state management something like useReducer would work. You can have as may labels as you like and define setter/getter actions that take the label as an argument.
Let analyze the two scenarios:
const labels = ['one', 'two'].map(useGetSet);
In this case the useGetSet is called inside the hook is called "inside custom React Hook function".
const labels = ['one', 'two'].map((l) => useGetSet(l))
In this case the useGetSet is called inside an anonymous function, so the "rule of hooks" is broken.
So basically:
In the first scenario: Hook > call of the hook
In the second scenario: Hook > anonymous function > call of the hook
Related to the last question:
Why this difference, shouldn't they be equivalent?
No, they are not equivalent.
In the first case the callback function reference is the function named, by you, useGetSet.
In the second case the callback function reference is a new function, defined anonymously.
An interesting explanation about why this rule is so important can be found in the documentation, in particular in this section:
https://reactjs.org/docs/hooks-rules.html#only-call-hooks-at-the-top-level
But also consider that there are cases when it is safe to disable the linter rule, as explained in depth here:
Why can't React Hooks be called inside loops or nested function?
In this example useGetSet is the callback.
const labels = ['one', 'two'].map(useGetSet);
In this example your calling the hook inside the callback so the error makes sense. This answer covers pretty nicely why the rule exists: Why can't React Hooks be called inside loops or nested function?
const labels = ['one', 'two'].map((l) => useGetSet(l));
You have 2 options here:
If you need to call the hook inside of a callback but the hook is not rendering anything then you need to create a new hook e.g https://codesandbox.io/s/sad-cache-zti5f?file=/src/TestComponent.jsx .
If the lambda is rendering smth at the end then you can create a new component
e.g https://codesandbox.io/s/quizzical-currying-pe15r?file=/src/TestComponent.js.
I am developing some web-app using React, just so that anybody reading can understand what is going on. That said, this is essentially a question regarding ES6 code optimization, rather then React and what have you.
TL;DR: I know that it reeeeeally won't make any goddamn difference because of how simple the whole thing is, but I am
wondering, in theory, what is the best approach? What is the 'good
practice' standard for declaring functions to be used within ES6 class
methods with most performance optimization?
What would be the most optimal place to define a "helper" function which is used by one of the methods of my class, for example:
class Table extends Component {
constructor() {
super();
this.instanceBasedFilter = function(){...} // Option 1
}
state = {};
render() {
const { filterProp } = this.props;
return (
<React.Fragment>
<table>
{this.filterMethod(filterProp)}
</table>
</React.Fragment>
);
}
prototypeBasedFilter(){...} // Option 2
filterMethod = filter => {
filter = filter || [];
function methodBasedFilter(){...} // Option 3
filter.filter(/* need to pass a function here*/)
};
}
function outsideBasedFilter(){...} // Option 4
So obviously, they are all different, overhead cannot be avoided in this case, I am just wondering which would be considered the best approach. For argument's sake lets disregard the option of placing a filter helper inside a different .js file, let's say that it is specific to this component.
My view on the options is bellow, correct me if I am wrong and suggest an option that is best practice if you know one.
Option 1:
A function object will be created every time the component is created and attached to the DOM, this can result in a moderate amount of "creations". On the plus side though, it will only take up memory space for as long as the component is displayed, once removed, memory is freed up, if I understand garbage collector properly.
Option 2:
A single function object will be created for use with all the components, so the processing is done only once, however it will be kept in memory for as long as the application runs.
Option 3:
A function object will be created every time the component updates, this can really end up being a lot of times, on the plus side though, as soon as the rendering is done, the memory is freed up, this options is what I'd intuitively go for if I was not thinking about optimizing, because i'd just inline an arrow function and be done with it. But it is the most overhead, no?
Option 4:
Now honestly this one has me most wound up... since I cannot demystify how it gets compiled. Placing a function declaration outside of the class itself exposes it to that class, and it's prototype, but I have no clue how does webpack/babel compile it. I'd make an educated guess that it is similar to option two in terms of optimization, since I'd assume its "held in scope" of some anonymous function that denotes a module environment, which means so long as the app is running it will never be collected, but also it will only be defined once.
So best practice?
I was playing with React.Children and children property and i found that there is no difference between using children.filter() and using React.Children.toArray(children).filter i was also able to render a specific child from the children props by using it's index directly and treat it like a normal array
so i wonder is there any difference between the two approaches? and if i can use the props directly with the arrays method why the react team implemented the toString() method in the first place
i tried console.log() the two of them and i see no defference between the two approaches
const ChildrenContainer = (props) => {
const { children, howMany } = props;
console.log(children, '\n', Children.toArray(children));
return (
<div className="card-columns">
{Children.map(children, (thisArg) => thisArg)}
</div>
);
}
If you look at the docs here: https://reactjs.org/docs/react-api.html#reactchildren you will notice some things.
React.Children deals with null and undefined. Calling children.filter() would error should children be null or undefined.
It deals with <Fragment> children
Includes helper functions such as .only() that provide more react specific capabilities than a regular array.
toArray() alters keys to preserve semantics of nested arrays.
So in your specific case, you might not notice any difference. But even if you are certain that children will always be a usable array, it is still good practice to use React.children. This is because it abstracts away the children concept so that your code is more future proofed (for changes to your code, and potential changes to react itself).
Say I have a PureComponent with a bunch of props. If I don't use every single prop in my render method, will it affect anything at all?
For instance, if I depend on componentWillReceiveProps being called for every single prop, even the ones that are not rendered...it will still be called, correct?
I can't imagine the React authors doing anything else... I can probably hunt down the code and take a peek myself and I will if I can't get a definitive answer here. Thanks.
NOTE: The reason I'm asking is because I'm wondering I see some code that is pulling unused variables from props here.
PureComponent is just like a regular component, only it implements the lifecycle method shouldComponentUpdate(). This function will ultimately determine if render() will run, so it cannot possibly rely on render() itself.
For instance, if I depend on componentWillReceiveProps being called for every single prop, even the ones that are not rendered...it will still be called, correct?
Yes, that is correct.
The code you referenced contains a common pattern, where props should be passed down, but not as a whole. The omitted props are commonly known as ownProps, which indicates that they belong solely to the component itself.
If we look at the code:
const {
element,
onChange: _onChange,
value: _value,
minLength: _minLength,
//...
...props
} = this.props;
In this case, value, minLength and debounceTimeout are only assigned to variables so they will be excluded from the props variable, which is used afterwards.
return React.createElement(element, {
...props,
//...
}
For instance, if I depend on componentWillReceiveProps being called for every single prop, even the ones that are not rendered...it will still be called, correct?
Yes componentWillReceiveProps will still be called regardless of what effect a particular prop may or may not have on rendering.
even the ones that are not rendered
Note many props are not rendered themselves, but they may have some effect on rendering (or they may not). React would not know if a particular prop affected rendering at least until the new virtual DOM was constructed.
We have a project using React + Redux + ImmutableJS. One of our engineers recently added a helper method to support destructuring an ImmutableJS Map when passing it to a component as props:
export function toObjectShallow(mapping: Map) {
const result = {};
mapping.map((value, key) => {
result[key] = value;
});
return result;
}
Thus, we can still do the following and avoid being verbose with repeated calls to Map.get:
<XyzComponent {...toObjectShallow(this.props.xyz)}/>
Yes, that's essentially making two shallow copies (our method + destructuring) of the original object. That should be of minimal expense. I'm wondering though, since I don't see this kind of recommendation really anywhere else in the React/Redux/Immutable communities, is there something else I'm missing that would make this unideal?
The passed properties are still the original, immutable props. It's just the containing object is mutated which doesn't matter, because it's not getting passed to the component anyways. So, what gives? This seems like such a simple solution while avoiding toJS(). Why isn't it really mentioned anywhere?
I followed the advice in the redux docs to use a HOC for all connected components to allow me to interact with plain javascript objects outside of redux. So my selectors and reducers still use ImmutableJS objects, but the rest of the code uses plain javascript objects:
https://redux.js.org/docs/recipes/UsingImmutableJS.html#use-a-higher-order-component-to-convert-your-smart-components-immutablejs-props-to-your-dumb-components-javascript-props
edit- not sure if this is the toJS you are mentioning above, I had assumed you meant ImmutableJS.toJS.
as far as preferences, using an HOC you only need to do it once per component, as opposed to each time you use a component in your method.
I think the "correct" answer I was looking for was to continue using Immutable JS's toObject() which does a shallow conversion of an Immutable Map object to a regular object, thus allowing us to continue using syntactic sugar while keeping the properties immutable and not having to use our own shallow copy.