What is the preferred syntax for a stateless react function? Why? - reactjs

Method 1:
const BasicProfileInfo = (props: BasicProfileInfoProps) => {
return (
<MainContainer>
{....}
</MainContainer>
)
}
Method 2:
function BasicProfileInfo(props: BasicProfileInfoProps){
return (
<MainContainer>
{....}
</MainContainer>
)
}
Project Environment:
babel-eslint: 8.0.2
babel-plugin-transform-class-properties: 6.24.1
babel-preset-es2015: 6.24.1
babel-preset-react-native: 4.0.0
react: 16.0.0
react-native: 0.48.4
mobx: 3.3.1
mobx-react: 4.3.3

Arrow function can be shortened to implied return:
const BasicProfileInfo = (props: BasicProfileInfoProps) => (
<MainContainer>
{....}
</MainContainer>
);
But it has a bit more footprint in ES5 output than regular function declaration, because an arrow is transpiled to regular function any way:
var BasicProfileInfo = function BasicProfileInfo(props) { return ... }
This is the only difference between them as stateless components. Arrow functions don't have their own this and arguments, but this isn't the case.

One advantage of using the 'arrow function' notation is that arrow functions don't have their own this value, which is useful if you want to preserve this from an outer function definition.
But, if your component is stateless, this doesn't matter, so it doesn't matter which one you use.

React components will use the function name as the displayName in debug messages and the developers console. The default displayName is Component, which is much less useful. This alone I think is enough to always prefer explicitly named functions (Method 2).
EDIT: As noted below, either of OP's methods will result in the displayName being populated correctly. The only situation that it will not is when exporting truly anonymous functions like: export default () => {}. So the other answers here are more relevant.

Related

React - Composition VS Configuration

I am currently working on a project that requires dynamically injecting one component into another.
My project is using Redux, so I came up with two possible solutions which both have their advantages and disadvantages, but I don't know which one to choose. I know that by nature, React encourages composition, but I'm still curious to know if the second approach (simpler and faster to use) is still good :
export const SlideOverComponents = {
'UserCreate': UserCreate,
'UserUpdate': UserUpdate,
};
The idea is to register all components that can be injected as a key value pair, and dispatch a Redux action with the key and the props required by this component.
{(!!componentKey && !!SlideOverComponents[componentKey]) && React.createElement(SlideOverComponents[componentKey], props)}
Then in my parent container, I just read this key and use the React.createElement to display the injected one.
This solution is working fine and is easy and fast to use because I just have to register any new component to the object to make it work.
Is this approach "ok" ? Or should I use composition ?
(I'm asking from a "good practice" or "anti-pattern" point of view.)
Yes that's fine, as long as the interface between all of the SlideOverComponents are completely identical. Your code is more verbose than it needs to be. You don't need createElement either if you assign it to a variable first
const Component = SlideOverComponents[componentKey]
return (
<div>
{Component && <Component {...props} />}
</div>
)
Edit:
I noticed that you are using TypeScript from other answers. Considering that, I still think you can use Composition but with types using String Literal Types like this:
type SlideOverComponentsType = "update" | "create";
type SlideOverComponentsProps = UserUpdateProps | UserCreateProps;
type SlideOverProps = {
key: SlideOverComponentsType;
} & SlideOverComponentsProps;
function SlideOver({ key, ...props }: SlideOverProps) {
switch (key) {
case "update":
return <UserUpdate {...props} />;
case "create":
return <UserCreate {...props} />;
default:
return null; // this will never happen but need to be addressed
}
}
And with an approach like that, you don't need an "Object" to store all the possible types of SlideOverComponents. You also guarantee that the props will always be using the proper interface and if eventually, you pass it wrongly TS will warn you about that.
Again: consider using types instead of declaring "options" as objects for cases like this.
Hope that this could help you or give you some good ideas!
Original Answer:
You can still use Composition for this and create some kind of check or `switch` statement inside the "Generic" Component. That way you could avoid adding so many checks(`if`s) outside of the parent component and guarantee that eventually non-existing `keys` could fallback to a default behavior or even to an error.
There are several ways of implementing it but one using switch that I like is this one:
function UserInteraction({ key, ...props }) {
switch (key) {
case "create": {
return <UserCreate {...props} />;
}
case "update": {
return <UserUpdate {...props} />;
}
default: {
return null;
// or you could thrown an error with something like
throw new Error(`Error: key ${key} not present inside component User`);
}
}
}
You could also use the Object.keys() method to accomplish almost the same behavior:
const UserInteractionOptions = {
"create": UserCreate,
"update": UserUpdate,
}
function UserInteraction({ key, ...props }) {
if (!Object.keys(UserInteractionOptions).includes(key)) {
return null;
// or you could thrown an error with something like
throw new Error(`Error: key ${key} not present inside component User`);
}
const InteractionComponent = UserInteractionOptions[key];
return <InteractionComponent {...props} />;
}
The main idea is to isolate the logic from deciding which component to render (and if it can be rendered) inside that component.
For future reading, you could check on TypeScript and how this can be easily handled by types, coercion, and the checks for non-present keys could be made before even the code runs locally.
A little of nitpicking: you are not "injecting" a Component inside another Component. You are just passing a key to deciding if the Parent Component renders or not the Child component through a flag. The injection of one Component into another involves passing the full component as a prop and just rendering it (or customizing it, eventually).
You could look at how React decides to render the children prop and how it decides if it is null, a string, or a ReactComponent to render an actual component. Also, a good topic to research is Dependency Injection.
As a simple example, injecting a component could looks like this:
function Label({ text }) {
return <p>{text}</p>;
}
function Input({ Label, ...props }) {
return (
<div>
<Label />
<input {...props} />
</div>
);
}

How can I get the JSX elements rendered by a component?

Short version: I have a component type (a class or a function) and props for it. I need to "render" the component to obtain its representation in JSX elements.
(I use the quotes because I mean «render into JSX elements» not «render into UI» and I am not sure about the terminology.)
Example:
const Foo = (props) => <div><Bar>{props.x + props.y}</Bar></div>;
// is an equivalent of `const elements = <div><Bar>3</Bar></div>;`
const elements = render2elements(Foo, { x: 1, y: 2 });
function render2elements(type, props) {
/* what should be here? */
}
Long version (for background story enthusiasts, may be skipped imo)
I have a React code whose very simplified version looks like this:
function Baby(props) {
/* In fact, it does not even matter what the component renders. */
/* It is used primarily as a configuration carrier. */
}
function Mother({ children }) {
const babies = getAllBabies(React.Children.toArray(children));
const data = parseData(babies);
return buildView(data);
}
function SomeOtherComponent(props) {
const { someProps1, someProps2,
someProps3, someCondition } = someLogic(props);
return (
<Mother>
<Baby {...someProps1} />
<Baby {...someProps2} />
{someCondition ? <Baby {...someProps3} /> : null}
</Mother>
);
}
It may be strange but it works. :) Until someone wants to do a little refactoring:
function Stepmother(props) {
const { someProps1, someProps2,
someProps3, someCondition } = someLogic(props);
return (
<>
<Baby {...someProps1} />
<Baby {...someProps2} />
{someCondition ? <Baby {...someProps3} /> : null}
</>
);
}
function SomeOtherComponent(props) {
return <Mother><Stepmother {...props} /></Mother>;
}
Now the Mother receives in its children only a JSX element for the Stepmother and can not parse the JSX elements for the Baby'ies. :(
So we return to my original question: I need to "render" Stepmother and then parse its internal JSX representation. But how can I do this?
P.S. I used functional components for brevity, but of course, all examples could use class components as well.
Thank you.
Don't do that.
I strongly encourage you to just rethink this solution altogether, ESPECIALLY if
It is used primarily as a configuration carrier.
...but.
So this kinda works however there's a couple of caveats:
if a component passed to that function is a class component and has some state, you won't be able to use any of it, in general it will probably cause a ton of issues that I'm not aware of
if a component passed is a function component, you can't use any hooks. It will just throw an error at you.
function render2elements(component, props) {
if (component.prototype.isReactComponent) {
return new component(props).render();
}
return component(props);
}
So if your "babies" are really simple this technically would work. But you just shouldn't refactor it the way you want and, again, ideally rethink this whole concept.

Babel does not transpile a JSX instance of an arrow function

my Javascript & React level is beginner.
I'm having fun with https://babeljs.io/repl to see how my JSX is transpiled into an older version of Javascript, and there is something I don't quite understand.
I'm trying to use an arrow function and an instance of this function :
const App = () => {
return <div></div>;
}
<App></App>
Which throws me an error :
repl: Unexpected token (5:6)
3 | }
4 |
> 5 | <App></App>
| ^
Note that a normal function AND it's instance () is working fine. An arrow function ONLY is working fine too. The problem happens when i use an arrow function AND the JSX instanciation of it.
Thank you !!
The problem happens when i use an arrow function AND the JSX instanciation of it.
That's the problem here. In order to be parsed correctly, <App></App> should be unambiguously identified as JSX. This cannot be done because a semicolon after an arrow was omitted.
It should be:
const App = () => {
return <div></div>;
};
<App></App>
As another answer mentions, JSX syntax is usually used in situations like ReactDOM.render(...) where it can be unambiguously identified as an expression, so this problem won't occur in this case.
Please also note that having <App></App> on file-level scope - while being technically correct - will result in discarding a value that it produces - no component will be mounted. Usually, one would use ReactDOM's render method to render React components' tree:
const root = document.getElementById("root");
ReactDOM.render(<App />, root)

Why ref='string' is "legacy"?

In the React documentation they say:
React also supports using a string (instead of a callback) as a ref prop on any component, although this approach is mostly legacy at this point.
https://facebook.github.io/react/docs/more-about-refs.html
Take the following example:
class Foo extends Component {
render() {
return <input onClick={() => this.action()} ref={input => (this._input = input)} />;
}
action() {
console.log(this._input.value);
}
}
Why should I prefer this, instead of:
class Foo extends Component {
render() {
return <input onClick={() => this.action()} ref='input' />;
}
action() {
console.log(this.refs.input.value);
}
}
?
It seems just much more clean and easier the second example.
Are there risks that the string method will be deprecated?
NB: I'm looking for the "official" answer to the statement in the documentation, I'm not asking about personal preferences and so on.
While perhaps more simple, the old refs API can get difficult in some edge cases, like when used in a callback. All kind of static analysis is a pain with strings, too. The callback based API can do everything the string API can do and more with just a little added verbosity.
class Repeat extends React.Component {
render() {
return <ul> {
[...Array(+this.props.times)].map((_, i) => {
return <li key={i}> { this.props.template(i) } </li>
})
} </ul>
}
}
class Hello extends React.Component {
constructor() {
super();
this.refDict = {};
}
render() {
return <Repeat times="3" template={i => <span ref= {el => this.refDict[i] = el}> Hello {i} </span>} />
{/* ^^^ Try doing this with the string API */}
}
}
Further discussion and a bit more comprehensive list of the possible issues with the string based api can be found from issue #1373, where the callback based api was introduced. I'll include here a list from the issue description:
The ref API is broken is several aspects.
You have to refer to this.refs['myname'] as strings to be Closure Compiler Advanced Mode compatible.
It doesn't allow the notion of multiple owners of a single instance.
Magical dynamic strings potentially break optimizations in VMs.
It needs to be always consistent, because it's synchronously resolved. This means that asynchronous batching of rendering introduces potential bugs.
We currently have a hook to get sibling refs so that you can have one component refer to it's sibling as a context reference. This only works one level. This breaks the ability to wrap one of those in an encapsulation.
It can't be statically typed. You have to cast it at any use in languages like TypeScript.
There's no way to attach the ref to the correct "owner" in a callback invoked by a child. <Child renderer={index => <div ref="test">{index}</div>} /> -- this ref will be attached where the callback is issued, not in the current owner.
The docs call the old string API "legacy" to make it clearer that the callback-based API is the preferred approach, as is discussed in this commit and in this PR which are the ones that actually put those statements to the documentation in the first place. Also note that a few of the comments imply that the string based refs api might be deprecated at some point.
Originally posted by danabramov on https://news.ycombinator.com/edit?id=12093234
String refs are not composable. A wrapping component can’t “snoop” on a ref to a child if it already has an existing string ref. On the other hand, callback refs don’t have a single owner, so you can always compose them.
String refs don’t work with static analysis like Flow. Flow can’t guess the magic that framework does to make the string ref “appear” on this.refs, as well as its type (which could be different). Callback refs are friendlier to static analysis.
The owner for a string ref is determined by the currently executing component. This means that with a common “render callback” pattern (e.g. <DataTable renderRow={this.renderRow} />), the wrong component will own the ref (it will end up on DataTable instead of your component defining renderRow).
String refs force React to keep track of currently executing component. This is problematic because it makes react module stateful, and thus causes weird errors when react module is duplicated in the bundle.

Using React.createClass instead of ES6 Classes (extends React.Component)?

Is there any harm in using React.createClass to define my components instead of using the ES6 approach where I extend from React.Component?
Below is an example where the Circle component is created using React.createClass and the Circle2 component is created using the ES6 class approach:
var Circle = React.createClass({
render: function() {
return (
<p>Hello</p>
);
}
});
class Circle2 extends React.Component {
render() {
return <p>Hello</p>;
}
}
I have read about the technical differences between both approaches, but am I doing something wrong by telling myself (and others) that using React.createClass is totally OK?
Thanks,
Kirupa
There's no harm in using React.createClass. It's still the official suggestion in the docs and the unofficial suggestion from the devs.
In fact, I'd go as far as to say that I think it's still a better solution than classes, for a few reasons.
Classes don't autobind (without hacks).
Adding static props is not intuitive.
You need third party libraries for mixins.
There are different lifecycle methods.
Object literal syntax is a very intuitive way to define properties and most Javascript developers will be very comfortable with it already.
I've heard it argued that the class syntax is more elegant, but with object literal shorthand and arrow functions, calls to createClass can be pretty expressive too.
const Circle = React.createClass({
render() {
return (
<p>Hello</p>
);
}
});
Of course, more than either of the others you should look for opportunities to use stateless functions instead.
const Circle = (props) => <p>Hello</p>;
They're the simplest option and they resolve a lot of the ambiguity above by simply not including it. It's just a function that takes props as arguments.

Resources