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.
Related
I have an application developed in Angular with typescript which has 20+ screens with list views of different entities, such as List of Movies, List of Actors, List of theatres etc. I have created a BaseListComponent<T extends Base> in angular and encapsulated most of the common functions such as view, filter, delete, search, pagination etc into this BaseListComponent. And I am creating children of it like MoviesListComponent extends BaseListComponent<Movies>. This way I need to add minimum functionality into children classes which reduces scope of errors and also efforts for unit test cases.
Now I am converting this application into React with typescript. I was studying about react hooks and learnt that many developers are preferring function based approach over classes based components. How can I create above structure with functions. As per my understanding there is no inheritance in functions. So I am doubtful if Generics will be feasible with functions. If not, how can I solve this problem using react's popular approach of decomposition?
Is there any guideline for using Generics or decomposition in react?
To do it "react style", first you have to make a component BaseListComponent. Then put the functions you listed "view, filter, delete, search, pagination etc" in this one. But whenever you want some functions to behave differently for deriving classes, you put a callback in that function.
For example:
renderView = () => { // function of BaseListComponent
const header = (
<div>--I am Header--</div>
);
const footer = () => (
<div>--------</div>
);
const letDerivedClassDecide = this.props.renderCenter();
return (
<div style={{ display: 'flex', flexDirection: 'column' }}>
{header}
{letDerivedClassDecide}
{footer}
</div>
);
}
This example means, you could control the basic layout as a vertical flex div, but you let the deriving class implement the this.props.renderCenter, so that its result can be combined with basic layout.
view = () => { // function of BaseListComponent
let someResult = 100;
someResult = this.props.view();
return someResult;
}
The same idea, this.props.view is implemented by deriving class.
Finally in the deriving class it would be like:
render() { // function of DeriveComponent; can be MoviesListComponent
return (
<BaseListComponent
renderCenter={this.renderCenter}
view={this.view}
>
);
}
Note that, the deriving class is not actually "derive" or extends the base component, it just composite it in the render() function. So both BaseListComponent and MoviesListComponent extends React.Component (or React.PureComponent when considering performance issue).
I recently switched from React to Vue. In React, i often had a method in a child component, that would look something like this:
onClick = (e)=>{
const val = e.target.value;
this.props.onClick(val)
}
I see that Vue is very "free", and allows you to treat component methods and props as "the same thing".
Is there some way to distinguish between the two, like with this.props? What is the convention regarding this issue?
Props, data, computed properties and methods end up as properties on Vue instance - members may also include lifecycle hooks when a component is declared as a class. As with any other class, their names may collide.
In case there are members that are private (onClick method) and are parts of public API (onClick prop), they can be named differently, e.g. with underscore naming convention that is common in JS OOP:
...
props: {
onClick: Function
},
methods: {
_onClick() {...}
}
...
The use of naming conventions for private properties is suggested by Vue style guide.
In composition API and Vue 3, there is a clear distinction in setup because props object is available, so it could be done similarly to how the question suggests. There is no difference between props and instance properties in templates so the rest of the answer is still applicable.
You should take advantage of another Vue technique, instead of passing a function, using its event emitter system:
// MyComponent:
methods: {
onClick(e) {
const val = e.target.value;
this.emit('click', val);
}
}
...
<button #click="onClick">Click me</button>
//When you actually use your component
<MyComponent #click="() => { console.log('delegated onClick!'); }"/>
As mentioned in one of the comments try to avoid using duplicate names for props, methods, etc...
I have a legacy Backbone app which I have begun to rewrite in React. The app has a main view containing two subviews, arranged vetically. The top panel displays some data, and the bottom one displays the result of some algorithm taking this data as input. Since I have many different data sources, each with a different algorithm applied to it, I have an abstract base View class, which I then subclass for each data source, adding, decorating and overriding methods as necessary. Somewhat like this:
// Base View.
const BaseView = Backbone.View.extend({
events: {},
initialize() {
this.subViewA = // instantiate subview...
this.subViewB = // instantiate subview...
},
generateResultData() {
// 'Abstract' method which should be specialised to generate data rendered by subViewB...
},
render() {
// render subviews...
},
});
// Derived View.
const Derived = BaseView.extend({
events: {
// event handlers...
},
add(a, b) {
return a+b;
},
// additional methods...
generateResultData() {
return {
result: this.add(2,2);
}
},
})
This results in a shallow hierarchy of many similar View classes. It's all terribly imperative, but it's a simple, intuitive and easy-to-reason-about pattern, and just works. I'm struggling to see how to achieve the same thing in React, however. Given that subclassing of subclasses of React.Component is considered an anti-pattern, my focus has naturally been on composition, and in particular Higher Order Components. HOCs (which I find beautiful, but unintuitive and often just downright confusing) seem to involve adding general features, rather than specialising/refining something more general. I have also considered passing in more specialised versions of Componenet methods through props. but that just means I have to use the same boilerplate Component definition over and over again:
// General functional component, renders the result of prop function 'foo'.
function GeneralComponent(props) {
const foo = this.props.foo || ()=>"foo";
return (
<div>
<span> { this.props.foo() } </span>
</div>
)
}
// Specialised component 1, overrides 'foo'.
class MySpecialisedComponent extends React.Component {
foo() {
return this.bar()
}
bar() {
return "bar"
}
render() {
return (
<GeneralComponent foo={this.foo} />
)
}
}
// Specialised component 2, overrides 'foo' and adds another method.
class MyOtherSpecialisedComponent extends React.Component {
foo() {
return this.bar() + this.bar()
}
bar() {
return "bar"
}
baz() {
return "baz"
}
render() {
return (
<GeneralComponent foo={this.foo} />
)
}
}
The above is a very simplistic case, obviously, but essentially captures what I need to do (though I would of course be manipulating state, which the example does not do, for simplicity). I mean, I could just do things like that. But I want to avoid having to repeat that boilerplate all over the place. So is there a simpler and more elegant way of doing this?
Generally, if a component is stateless and doesn't use lifecycle hooks, there are no reasons for it to be Component class. A class that acts as a namespace and doesn't hold state can be considered an antipattern in JavaScript.
In constrast to some other frameworks, React doesn't have templates that would need to map variables in order for them to be available in view, so the only place where bar function needs to be mentioned is the place where it's called. JSX is an extension over JavaScript, JSX expressions can use any names that are available in current scope. This allows to compose functions without any classes:
const getBar => "bar";
const getBaz => "baz";
const getBarBaz => getBar() + getBaz();
const MySpecialisedComponent = props => <GeneralComponent foo={getBar} />;
const MyOtherSpecialisedComponent = props => <GeneralComponent foo={getBarBaz} />;
An anonymous function could be passed as foo prop instead of creating getBarBaz but this is generally discouraged because of unnecessary overhead.
Also, default prop values could be assigned with defaultProps without creating new ()=>"foo" function on each component call:
function GeneralComponent({ foo }) {
return (
<div>
<span> {foo()} </span>
</div>
)
}
GeneralComponent.defaultProps = { foo: () => 'foo' };
IMO what is throwing you off isn't inheritance vs composition, it's your data flow:
For example, many of my derived views need to do custom rendering after the main render. I'm using a third-party SVG library, and the data rendered into the 'result' subview is derived from analysis of rendered SVG elements in the main data view above it
So what you're trying to do here is have a child update props of a distantly related component after render, correct? Like this?
// after the svg renders, parse it to get data
<div id="svg-container">
<svg data="foo" />
<svg data="bar />
</div>
// show parsed data from svg after you put it through your algos
<div id="result-container">
// data...
</div>
There's a lot of state management libraries out there that will help you with this problem, that is, generating data in one component and broadcasting it to a distantly related component. If you want to use a tool built-in to react to address this you may want to use context, which gives you a global store that you can provide to any component that wants to consume it.
In your example your child classes have data-specific methods (add, etc.). IMO it's more typical in react to have a generic class for displaying data and simply passing it down map functions as props in order to rearrange/transform the rendered data.
class AbstractDataMap extends PureComponent {
static defaultProps = {
data: [],
map: (obj, i) => (<div key={i}>{obj}</div>)
};
render() {
const { data, map, children } = this.props;
const mapped = data.map(map);
return (
<Fragment>
{mapped.map((obj, i) => (
children(obj, i)
))}
</Fragment>
);
}
}
// in some other container
class View extends Component {
render() {
return (
<div>
<AbstractDataMap data={[1, 2, 3]} map={(n) => ({ a: n, b: n + 1 })}>
{({ a, b }, i) => (<div key={i}>a: {a}, b: {b}</div>)}
</AbstractDataMap>
<AbstractDataMap data={[2, 4, 6]} map={(n) => (Math.pow(n, 2))}>
{(squared, i) => (<div key={i}>squared: {squared}</div>)}
</AbstractDataMap>
</div>
);
}
}
IMO this pattern of using an HOC to abstract away the labor of explicitly using .map in your render calls (among other uses) is the pattern you are looking for. However, as I stated above, the HOC pattern has nothing to do your main issue of shared data store across sibling components.
Answering my own question, which I've never donw before...
So my question really arose from a concern that I would need to refactor a large, imperative and stateful codebase so as to integrate with React’s composition-based model (also with Redux). But it occurred to me after reading the (very insightful and helpful) responses to my question that my app has two parallel parts: the UI, and an engine which runs the algorithms (actually it's a music analysis engine). And I can strip out the Backbone View layer to which the engine is connected quite easily. So, using React’s context API I've built an ‘AnalysisEngineProvider', which makes the engine available to subcomponents. The engine is all very imperative and classically object-oriented, and still uses Backbone models, but that makes no difference to the UI as the latter has no knowledge of its internals - which is how it should be (the models will likely be refactored out at some point too)...
The engine also has responsibility for rendering the SVG (not with BB views). But React doesn’t know anything about that. It just sees an empty div. I take a ref from the div and pass it to the engine so the latter knows where to render. Beyond that the engine and the UI have little contact - the divs are never updated from React state changes at all (other components of the UI are though, obviously). The models in the engine only ever trigger updates to the SVG, which React knows nothing about.
I am satisfied with this approach, at least for now - even if it's only part of an incremental refactor towards a fully React solution. It feels like the right design for the app whatever framework I happened to be using.
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.
It seems like that in ReactNative that you can do something like new SomeComponent(props) and return someComponent.render(). I'm guessing this is basically what the JSX does when you directly declare <SomeComponent someProp="etc"/>? If that's the case, is there any potential downsides to manually manipulating the components rather than using JSX?
It seems like that in ReactNative that you can do something like new
SomeComponent(props) and return someComponent.render()
Not really. If you manually instantiate a component class, among other things lifecycle methods (componentWillMount(), componentDidMount()) won't work.
I'm guessing this is basically what the JSX does when you directly
declare
Have a look at what babel compiles to, but basically JSX compiles down to plain js:
class Hello extends React.Component {
render() {
return <div>Hello {this.props.toWhat}</div>;
}
}
translates to:
class Hello extends React.Component {
render() {
return React.createElement('div', null, `Hello ${this.props.toWhat}`);
}
}
If that's the case, is there any potential downsides to manually
manipulating the components rather than using JSX
You could use React.createElement and completely avoid JSX, but in my experience this is harder and more verbose. Manually instantiating components though like you initially suggested defeats the purpose of using React :)
Some good links which go in more detail:
https://reactjs.org/docs/react-without-jsx.html
https://reactjs.org/docs/react-component.html