Direct call of a functional component

2020-02-01 07:47发布

Stateless functional component is just a function that receives props and returns React element:

const Foo = props => <Bar />;

This way <Foo {...props} /> (i.e. React.createElement(Foo, props)) in parent component could be omitted in favour of calling Foo directly, Foo(props), so React.createElement tiny overhead could be eliminated, yet this isn't necessary.

Is it considered a bad practice to call functional components directly with props argument, and why? What are possible implications of doing this? Can this affect the performance in negative way?

My specific case is that there's some component that is shallow wrapper over DOM element because this was considered a good idea by a third party:

function ThirdPartyThemedInput({style, ...props}) {
  return <input style={{color: 'red', ...style}} {...props} />;
}

Here's a demo that shows this case.

This is widely accepted practice but the problem with it is that it's impossible to get ref of wrapped DOM element from stateless function, so the component uses React.forwardRef:

function withRef(SFC) {
  return React.forwardRef((props, ref) => SFC({ref, ...props}));
  // this won't work
  // React.forwardRef((props, ref) => <SFC ref={ref} {...props } />);
}

const ThemedInput = withRef(ThirdPartyThemedInput);

This way it can be used as:

<ThemedInput ref={inputRef} />
...
inputRef.current.focus();

The obvious downside I'm aware of is that withRef requires a developer to be aware of wrapped component implementation, which isn't a usual requirement for HOCs.

Is it considered a proper approach in a situation like described above?

2条回答
兄弟一词,经得起流年.
2楼-- · 2020-02-01 08:19

Functional components are very useful when you don't need to use any of the lifecycle method or don't need to update the component state. As far as you don't need to them, you're good and yet best to go with stateless component.

This will not hit the performance issue but gain the profit regarding its performance because we're just simply using function to render the component and not caring for its update, mounts, receive props, etc. But still there's no 100% gain using stateless component because react internally use class to render them.

It's about 45% improvement.

This post will also guide which one to choose between statefull component and stateless component.


Further, you can not only receive the props but can also receive the ref:

const stateless = (props, ref) => <ReturnComponent {...props} ref={ref} />

Okay, let me refine my statement. Most of the blogs and even the docs states that stateless component don't have ref. Here are a few Q/A prepared regarding this issue:

Do I need to use statefull component just to use ref?

No. I already mentioned that we must require the class based component if we have to work with component state or hook some lifecycle method.

How can I create ref in stateless component?

const stateless = () => {

  // we can't do this.myRef = React.createRef()
  // so, let's create an object
  const RefObj = {}

  // now, create ref in {RefObj}
  RefObj.myRef = React.createRef()

  return <input type="text" ref={myRef} />
}
查看更多
家丑人穷心不美
3楼-- · 2020-02-01 08:40

I don't think there's anything wrong with calling Stateless Functional Component directly. As you said it's even one tiny overhead eliminated. As to the possible implications, it would be bold to say that there are none implications and there will be none implications in the future because this is a really rare way of using SFC's. But everything points to conclusion that there shouldn't be any implications (it's just one function call less).

Anyway, below I'd like to present another way of doing this using findDOMNode instead of refs:

I've created Focus component that is really convenient to use but needs to be initialized first (since we need a way to trigger focus outside props since a component may be rerendered with the same props):

// focus.js
import React from "react";
import { findDOMNode } from "react-dom";

export default function createFocus() {
  class Focus extends React.Component {
    componentDidMount() {
      Focus.now = () => {
        findDOMNode(this).focus();
      }
    }
    render() {
      return this.props.children;
    }
  }

  return Focus;
}

// index.js
import React, { Component } from 'react';
import { render } from 'react-dom';
import createFocus from './focus';

const Focus = createFocus();

import { ThirdPartyThemedInput } from './third-party-lib';

function App() {
  return (
    <div>
      <button onClick={() => Focus.now()}>Proceed with form</button>
      <Focus>
        <ThirdPartyThemedInput placeholder="Fill me" />
      </Focus>
    </div>
  );
}

render(<App />, document.getElementById('root'));

live at: https://stackblitz.com/edit/react-bpqicw

查看更多
登录 后发表回答