I am a beginner to react and finding myself confused between mixin and decorators. Can somebody elaborate? Thanks.
They both extend and/or override methods of the React Component. They are used to share common functionality between components, in places where extending a class would not work and is not intended.
An example would be the PureRenderMixin, which overrides the shouldComponentUpdate method and compares the props of a component to decide, if a rerender should be executed.
However, mixins are deprecated and do not work with the ES6 syntax of React anymore. Your options are either to use inheritance or decorators to achieve the same result.
Example
Here is an example of the (kind of) PureRenderMixin, using a decorator. Also I used Immutable.js.
// pure.js
import React from 'react';
import assign from 'object-assign';
import {is} from 'immutable';
/**
* Pure Render Decorator
* #param props
* #returns {function()}
*/
export default (...props) => (Component) => class PureComponent extends React.Component {
shouldComponentUpdate(nextProps) {
if (!props.length) {
props = Object.keys(nextProps);
}
for (let i = 0, l = props.length; i < l; i++) {
if (!is(nextProps[props[i]], this.props[props[i]])) {
return true;
}
}
return false;
}
render() {
return React.createElement(Component, assign({},
this.props,
this.state
));
}
}
The general usage of the decorator would be #pure(params).
params can contain the name of the props or can be empty. In the decorator you see the ...props as a parameter. This is where the params are passed in.
The parameter Component of the inner function gets the React Component passed in, on which you use the decorator.
You can use the decorator in your component as follows:
import React from 'react';
import pure from './pure.js';
#pure('myProp', 'anotherProp')
export default MyComponent extends React.Component {
static propTypes = {
myProp: React.PropTypes.any,
anotherProp: React.PropTypes.any
}
render() {
return <p>I only re-render, when my props changed.</p>;
}
}
What does it do?
The decorator overrides the shouldComponentUpdate method of the component. Everytime the React Component calls its shouldComponentUpdate method, it now uses the one provided in the decorator.
The decorator itself compares the props of the Component to the next props that it is going to receive. Only if the props change, the component will update.
This is nice, because it prevents unnecessary rendering - that's great for performance!
You see, decorators are basically functions that take parameters (such as the React Component) and modify them in order to make code reusable. It takes a bit of getting used to, but its no rocket science. :-)
If there are any more questions, please feel free to ask!
Related
I'm trying to get to grips with the new useContext function in React. Works great in stateless functionality components. For example:
import React from 'react';
import LocaleContext from '../LocaleContext';
const Link = ({ text, url }) => {
const locale = useContext(LocaleContext);
return (
<a href={`/${locale}/${url}`}>
{text}
</a>
);
};
export default Link;
I also want to use useContext in stateful components, and even non React functions, but when I do so, I get the following error:
Hooks can only be called inside the body of a function component.
The message seems simple enough to understand, but is this really true? I can only use it in a stateless functional component? If so, it seems kind of pointless to me, because it's super easy to use a simple HOC or the traditional method of:
<Locale Consumer>
{locale => (
...
)}
</LocaleConsumer>
So what gives here? I have the latest version of every package in my project. Not sure if it matters but I'm developing a NextJS site here.
If you really want to use classes (i actually came from Angular and i still prefer use classes) you can workaround easily like that:
class ComponentImpl extends React.Component<any> {
constructor(props?) {
super(props);
}
render() {
return (
<div>
CounterButton: <button onClick={() => {this.props.appContext.setCount(this.props.appContext.count + 5)}}>App Counter + 5</button>
</div>
)
}
}
export function Component() {
let appContext = useContext(AppContext);
return <ComponentImpl appContext={appContext}></ComponentImpl>
};
And you just use it: <Component></Component>
The problem is what the error says. React hooks aren't available in class components. Due to differences between class components and function components, hooks cannot be used with the former.
As the documentation says,
Hooks let you use more of React’s features without classes. Conceptually, React components have always been closer to functions. Hooks embrace functions, but without sacrificing the practical spirit of React. Hooks provide access to imperative escape hatches and don’t require you to learn complex functional or reactive programming techniques.
Hooks are supposed to address common use cases that are specific to class components which couldn't be previously implemented with stateless functional components alone. Functional components aren't stateless since React 16.8 and are allowed to have a state and trigger own updates.
As for useContext hook,
When the provider updates, this Hook will trigger a rerender with the latest context value.
It would be messed up in class component due to difference between functional and class components. Component function is called each time the component is rendered:
const Foo = props => {
const context = useContext(Context);
// use context
}
There's no place in class component that would behave the same way except render function. And if lifecycle-specific tasks go to render function, this means that a class was a wrong choice, and class component needs to be refactored to a function. A counterpart to useContext in class components is contextType, which is currently restricted to single context.
For multiple contexts it's still required to receive them through context Consumer inside render, or as props from higher-order component wrapper:
const contextsHOC = (contexts = {}) => Comp => (
props => {
const contextProps = {};
for (const prop in contexts) {
// eslint-disable-next-line react-hooks/exhaustive-deps
contextProps[prop] = React.useContext(contexts[prop]);
}
return <Comp {...props} {...contextProps}/>;
}
);
#contextsHOC({ bar: BarContext, baz: BazContext });
export default class FooComponent extends Component {
// contexts are mapped to this.props.bar and this.props.baz
...
}
// or
class FooComponent extends Component { ... }
export default contextsHOC({ ... })(FooComponent);
Passing contexts as props allows for additional optimization with PureComponent or shouldComponentUpdate.
useContext is a hook that consumes a context and can only be used in functional components.
If you want to consume context in class components, you will need to look at alternative methods such as Consumer Component, official docs for this here
I have a couple question about prop-types in React:
1. when should we use react props-type checking in component, do we have to use it in all component with props passed in ?
2. can props-type checking be applied in both stateless component and class component ?
You can use prop-types in whatever components you want prop validation to occur. It doesn't have to be used in every component that uses props, although it's generally a good idea to do so.
For a class component, you can do:
class Component extends React.Component {
static propTypes = {
// prop types here
}
// component stuff
}
For a functional component, you can do:
const Component = (props) => {
// component stuff
}
Component.propTypes = {
// prop types here
}
When should you use them? Whenever you want. Might be nice to add them if you're sharing these components with other developers (at work or on npm)
Can you use them in both functional and class components? Yes.
I've seen code like this
function abc(){
return 'abc'
}
class MyComponent extends React.Component {
static abc = abc;
render() { return <h1>{this.abc}</h1>; }
}
where function abc is defined outside of a react class. I have no clue why the author did it that way, why can't just do it within the class?
These are ES6 static methods and are not exclusive to React. They are members of the component class and not of instances of the component. They are not used extensively in React, but they can be useful. It is even mentioned in the React docs:
Sometimes it’s useful to define a static method on a React component.
For example, Relay containers expose a static method getFragment to
facilitate the composition of GraphQL fragments.
They can be used as common members of the Component, shared by all instances of it. To give you an idea, other static members of a React class are displayName and defaultProps.
Also see Static methods in React. As you can see, there aren't many cases where you would use a static method.
For one thing declaring functions outside the class are easier to EXPORT. Which serves a great deal when testing your react application i.e. through jest.
import React, { Component } from 'react';
class Contacts extends Component {
render() {
return (
Contact()
);
}
}
const Contact = () => {
return (
<div>
<p>Contactssssss</p>
</div>
);
};
export default Contacts;
I used to do React.createClass but since I heard it's faster with extends component and since I start to play with babel now, I would like to convert stateless legacy component with the newer style.
I have this
const Main = React.createClass({
render(){
return(<h1>my title</h1>)
}
})
so what's the 'newer' syntax for above code?
is it like this?
const Main = () => {
render(){
return(<h1>my title</h1>)
}
}
Then when to use React.component?
class Main extends React.Components() {
render(){ return(<h1>something</h1>}
}
There is not performance difference between using
const Main = () => {
return(<h1>my title</h1>)
}
or
class Main extends React.Component {
render(){ return(<h1>something</h1>}
}
given they receive the same props, however the react creators suggest that performance improvements will be made in future. So in short,
Use the first approach, which is a functional component approach when your component only takes in props and renders the result. However use the ES6 class based approach, when your React component has more functionality and handles states.
In your case you are just rendering the component so a functional component approach will be best suited for you
Both of your example is correct. Just that the extends Component should be without s and need to import React, { Component } from 'react'; at the top.
const Main = () => {
return(<h1>my title</h1>)
}
You generally want to use the above example when it is does not need any lifecycle methods. This are refer to as Pure component that will always behave the same when given the same props. It also does not keep state.
class Main extends React.Component() {
componentWillMount() {
console.log("I can be not pure");
}
render(){ return(<h1>something</h1>}
}
The above are Component that could manage state and use lifecycle methods.
Usually to decide what to use, I always start with a pure component. When continue developing, I discover that I have to use state or lifecycle methods, I would then turn it to a component.
I've been using this.props and props interchangeably, and for the most part, there doesn't seem to be a major difference between them (as far as I can tell).
However, I've been running into issues lately that make me think that when and why one is used over the other matters.
This is where I'm currently using them interchangeably:
constructor(props, context) {
super(props, context);
this.data = props.card.data;
this.dataLength = this.props.card.data.length;
}
What's the difference and when do you use one over the other, and where?
Thanks!
It all depends on the type of component you are using.
const StatelessComponent = (props) => {
return <div>{props.something}</div>;
}
class SomeComponent extends Component {
constructor(props){
super(props)
}
render() {
return <div>{this.props.something}</div>;
}
}
Here you will notice that in the stateless component it is just a regular function without any this context. The props are passed to the function so we already have access to it.
When you have a class you have a this context that the props live on.
In the constructor of a class the props are passed to the class constructor. So in the context of that constructor function props is passed as an argument and is a local variable
I would recommend you stick to a pattern, when you have props passed as an argument to a function you use props when you are in other methods of a react class you use this.props. That is how it was intended to be used. Also there is something to be said for consistency, so whether you choose one or the other stick with that pattern. It can be confusing if you don't follow a pattern / keep things consistent.
Only in the constructor of React ES2015 components (non-function components) will you be able to refer to props as it is passed into the constructor. If you do this.props === props in the constructor, it will evaluate to true.
However in React, you can only use this.props after you call super(props). This is part of the ES2015 Class spec.
For simplicity, I usually just use solely props within the constructor, and this.props within the component lifecycle methods and in render.
In simple terms:
1.props.data is used in functional components
2.this.props.data is used in Class components
copy the code and run it in "stackblitz" ->https://stackblitz.com/edit/react-3wr8jb
the following code shows usage of both class and functional components with props.data and this.props.data
import React, { Component } from 'react';
import { render } from 'react-dom';
//=================PROPS=======================
//Both Class and Functional components have "properties" ie props
//properties are immutable->fixed->cant be changed
//=================================================
//props.data in functional components
function A1(props)
{
return <h2>functional component->props.data:{props.data}</h2>
}
//===============================================
//this.props.data in class components
class A2 extends React.Component{
render()
{
return<h2>class component->this.props.data:{this.props.data}</h2>
}
}
//===================================================
var element1=
<div>
<hr/>
//data from class and functional component
<A1 data="Sample data" />
<A2 data="Sample data"></A2>
<hr />
</div>
render(element1, document.getElementById('root'));