how to set displayName in a functional component [React] - javascript

I know that setting the displayName is sometimes required especially when you're dealing with production builds. I want to know how to set it using my functional component - is it possible/allowed?
Here's what I've got in my class component:
const MyComponent = React.createClass({
displayName: 'HeyHey',
render: function() {
console.log(this.displayName);
}
});
How do I do the same thing inside a stateless component?

The docs for displayName say
The displayName string is used in debugging messages. Usually, you don’t need to set it explicitly because it’s inferred from the name of the function or class that defines the component. You might want to set it explicitly if you want to display a different name for debugging purposes or when you create a higher-order component, see Wrap the Display Name for Easy Debugging for details.
In your case, you would simply use
const MyComponent = (props) => { ... }
MyComponent.displayName = 'HeyHey'
Or you can use Object.assign
const MyComponent =
Object.assign
( props => { ... }
, { displayName: 'HeyHey' }
)

Figured it out
const MyComponent = props => {
return (
<p>How you doin?</p>
)
}
MyComponent.displayName = "MyComponent"

React either needs displayName for functional components when they're defined as arrow functions, or the name of the function itself.
So for arrow functions:
const SomeComponent = () => <p>I come from an arrow function</p>
SomeComponent.displayName = 'HeyHey'
If you use a function, it'll use its name as displayName without having to define it separately:
function HeyHey() { return <p>I come from a non-arrow function!</p> }

Related

React Hooks TypeScript inheriting parent state & function types in child

I'm looking for ways to inherit parent state & function types in child component so that I do not have to redefine them in child component.
I have parent component as follows:
const TagPopupModal: React.FC = () => {
// state
const [addTagPressed, setAddTagPressed] = useState<boolean>(false);
const [tagList, setTagList] = useState<userTagType['tags'][]>(
[],
);
// function
function addToTagList(tag: tagListType) {
...
}
return (
...
<TagListView
addTagPressed={addTagPressed}
tagList={tagList}
addToTagList={addToTagList}
/>
)
}
In parent component, I have defined the types for the state and parameter type for the function. Those are passed to child component.
Now in child component I have following:
interface PropTypes {
addTagPressed:boolean;
tagList: Array<userTagType['tags']>;
addToTagList: (value: taglistType) => void;
}
const TagListView: React.FC<PropTypes> = ({
addTagPressed,
tagList,
addToTagList,
}) =>{
...
}
As you can see I had to define the same types for the props again, but using interface, in the child component. This is time-consuming, is there ways to directly inherit the types of the state and functions from the parent?
What would be the most efficient way?
Thanks,
You could export your interface for PropTypes and consume that elsewhere, e.g. as const [tagList, setTagList] = useState<PropTypes['tagList']>([]);
You won't necessarily save much raw keyboard mashing, but it will help keep your types consistent and reusable.
import { TagListView, PropTypes } from '../TagListView';
const TagPopupModal: React.FC = () => {
// state
const [addTagPressed, setAddTagPressed] = useState(false);
const [tagList, setTagList] = useState<PropTypes['tagList']>(
[],
);
// function
const addToTagList: PropTypes['addToTagList'] = (tag) => {
...
}
return (
...
<TagListView
addTagPressed={addTagPressed}
tagList={tagList}
addToTagList={addToTagList}
/>
)
}
Note that you don't need to be super vigilant about declaring explicit types in general. useState(false) for example will automatically infer the type of that state as boolean. And if you declare the function addToTagList as a const and set its type to be what is defined in PropTypes, TS will automatically know that the parameter tag is supposed to have a given type.

How to type props in a React PureComponent using hooks in TypeScript?

I want to convert a PureComponent to a memoized FunctionalComponent, so it only re-renders if the props change, even if the parent re-renders.
export class MyComp extends React.PureComponent<{param: string}> {
public render() {
return <div>{this.props.param}</div>;
}
}
I want to change it so it's a functional component in order to use React Hooks.
export const MyComp: React.FC<{ param: string }> = useMemo(({param}) => {
return <div>{param}</div>;
}, [param]);
But the above doesn't work and there are several problems:
The destructed param is type is any and not correctly inferred.
I can not pass [param] as the dependencies list for useMemo because it was not defined in this context.
There seems to be no way to set the type of the parameters in the dependencies list. Is this because the parameters are just variables from the parent scope and not actual arguments that are passed in? If yes, how can we export a pure component if we don't know what props will be passed in?
Does it make more sense to have something like this?
export const MyComp: React.FC<{ param: string }> = (param) => {
return useMemo((param) => {
return <div>{param}</div>;
}, [param]);
};
Is this component memoized correctly? What if we also have some internal state our data from store, will it re-render when those change?
export const MyComp: React.FC<{ param: string }> = (param) => {
return useMemo((param) => {
// Will it re-render when this changes even if it's memoized?
const fromStore = useSelector((state: IState) => state.myValue));
return <div>{param} {fromStore}</div>;
}, [param]);
};
I don't think it will rerender if the store value changes. But in that case we would have to hoist fromStore outside useMemo, but doesn't this mean that the component is not pure anymore? As whenever the parent re-renders the MyComp function will run again (eg. compute fromStore value again).
I do like working with hooks, but their functionality and implementation is a bit abstract. What's the correct way of implementing a typed pure component with hooks?
You are using the wrong method here, React.memo is the equivalent of React.PureComponent.
React.useMemo is used to memoize expensive computations inside a function component.
import React, { memo } from 'react'
type Props = {
param: string
}
export const MyComp = memo(({ param }: Props) => (
<div>{param}</div>
))
Also, many people prefer to not type components with React.FC, you can read why here

How to pass arguments to a function inside compose?

I have a function like so:
export const fireView = (prop1, prop2) => WrappedComponent =>
class extends Component {
//stuff in here
}
then a hoc like this:
export const withFireView = (prop1, prop2) =>
fireView(prop1, prop2)
but now I want to put this function inside compose as I need to call it with mapStateToProps
so I did this:
compose (
connect(mapStateToProps),
fireView
)
but it breaks because You must pass a component to the function returned by connect
so I then I get rid of the arguments in the fireview function and I think this will work but then all the arguments are now undefined inside the function as I've not passed them
Is there any way to do something like this:
compose (
connect(mapStateToProps),
fireView(arg1, arg2)
)
but obviously, they are not defined there if that makes sense.
Here is a full working example:
var Component = React.Component;
var render = ReactDOM.render;
var Provider = ReactRedux.Provider;
var connect = ReactRedux.connect;
var createStore = Redux.createStore;
var compose = Redux.compose;
const reducer = () => {return {}};
const mapStateToProps = () => {
return {};
};
const wrapped = (props) => {
return <div>{props.prop1} {props.prop2}</div>;
}
const fireView = (prop1, prop2) => WrappedComponent => {
return class extends Component {
render() {
return <WrappedComponent prop1={prop1} prop2={prop2} />;
}
}
}
const withFireView = (prop1, prop2) => fireView(prop1, prop2);
const withFireViewAndConnect = (arg1, arg2) => compose(connect(mapStateToProps), withFireView(arg1, arg2));
const App = withFireViewAndConnect('some', 'arg')(wrapped);
render(<Provider store={createStore(reducer)}><App /></Provider>, document.getElementById('demo'));
<script src="https://cdnjs.cloudflare.com/ajax/libs/babel-polyfill/6.13.0/polyfill.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.6.3/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.6.3/umd/react-dom.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/redux/4.0.1/redux.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-redux/6.0.0/react-redux.js"></script>
<div id="demo"></div>
I had a similar requirement and ended up on this page. I supposed we were not able to understand the use case. I will try to summarise it and also provide an answer to it.
Explanation
We can use HOC (Higher Order Components) in React to abstract out common functionality. In React docs, we can see the 'with' notation to use these HOCs. I have created a few on my app as well.
withSplashScreenRedirect, withHistoryObject
All these HOCs that I created takes a Component (WrappedComponent) and returns a Component. (No additional Arguments required).
const EnhancedComponent = higherOrderComponent(WrappedComponent);
I would generally have around 2 to 3 HOCs on each Screen level Component. Writing these HOCs as functions returning other functions look ugly. Redux provides a useful compose function. Compose helps us to chain these HOCs in a more readable manner.
From compose docs.
All compose does is let you write deeply nested function transformations without the rightward drift of the code. Don't give it too much credit!
Soon I wanted to create another HOC that would standardise my API call logic and API call handling logic. In this case, I had to send two additional functions to my HOC (along with the Wrapped Component).
withApiCallHandler(WrappedComponent, makeApiCall, handleApiResponse)
Passing props to HOC is not a big deal. React Docs have a good example explaining how to do it. However, adding such an HOC to compose chain is not so straightforward. I guess that is what OP was trying to ask here.
Answer
I wasn't able to find a way to chain such a HOC with compose. Maybe it's not possible. The best solution would be to initialise the withApiCallHandler Component separately and then chain it with compose.
Instead of
const ComposedScreenComponent = compose(
withHOC1,
withHOC2,
withHOC3(arg1, arg2)
)(ScreenComponent);
Do this
const withHOC3AndArgs = (WrappedComponent) =>
withHOC3(WrappedComponent, arg1, arg2);
const ComposedScreenComponent = compose(
withHOC1,
withHOC2,
withHOC3AndArgs(arg1, arg2)
)(ScreenComponent);
This is not the best answer to the question, but if someone is stuck then this solution will surely be a good workaround.
You can make withFireView return a function instead. All will work.
export const withFireView = (prop1, prop2) => () => fireView(prop1, prop2)

Blacklist React components

Is there a way to define a function to hook before each component in my app is mounted?
The idea is that if a component is blacklisted it doesn't mount at all.
The solution must leave the components unmodified for backward compatibility and should run in production (so rewire and other testing tools are probably off the table but open to suggestions :) )
Example
//something like this...
ReactDOM.beforeEachComponentMount( (component, action) => {
if(isBlacklisted(component)){
action.cancelMountComponent();
}
}
Could you write a simple Babel plugin that transforms blacklisted components to a noop functional component () => {} at compile time?
You could wrap the required components inside a higher order component that checks whether the component is blacklisted or not.
for example :
class YourComponent extends Component {
constructor(props){
super(props);
}
render(){
return(
// your component goes here ..
);
}
}
export default WithPermission(YourComponent);
check if the component needs to be rendered or not inside the HOC WithPermission.
function withPermission(YourComponent) {
class WithPermission extends React.Component {
constructor(props) {
super(props);
}
// you can check the props inside ComponentDidMount and set a flag if
// the component satisfies the criteria for rendering.
render() {
const {blacklistedComponents,...rest} = this.props;
if(!blackListedComponents){
return <YourComponent {...rest} />
}
else{
return null;
}
}
}
}
There is no such functionality out of box.
You may shim React rendering cycle, I mean shim React.createElement method and validate component before it is added to VDOM
All JSX is processed through React.createElement
e.g. at the start of app add
let React = require('react');
let originalCreateElement = React.createElement;
React.createElement = function() {
let componentConstructorOrStringTagName = arguments[0];
if (isBlacklisted(componentConstructorOrStringTagName)) {
return null;
}
return originalCreateElement.apply(this, arguments);
}
The best idea I can think of is to "shim" react and Component
if you are using webpack you can use this:
https://webpack.js.org/guides/shimming/
in the bottom line that means instead of importing react you will import your own class of react.
In your new class you could extend React Component and place a check on the render function or something similar.
You could implement a custom ESLint rule and catch this as soon as a dev tries to use a blacklisted components. The id-blacklist rule is similar to what you want, but at the identifier level. The source code looks simple. Maybe you can adapt it to disallow more then just identifiers.
Consider the following solution:
Let there be a file where you declare which components are blacklisted:
let blacklist = [{
name: 'secretComponent',
invoke: (props)=> {
return <SecretComponent ...props />
},
isBlacklisted: true
},{
name: 'home',
invoke: (props)=> {
return <HomeComponent ...props />
},
isBlacklisted: false
},{
name: 'login',
invoke: (props)=> {
return <LoginComponent ...props />
},
isBlacklisted: false
}];
Define a Higher Order Component like below:
function renderIfNotBlacklisted(name) {
let component = blacklist.map(x=> x.name == name); //blacklist from above
if (!component.isBlacklisted){
return component.invoke();
} //else can be handled as you will
//You can keep a default component to render or send empty values
}
Call this component in the render function wherever you want this feature to work. This way you have a centralized location to managed blacklisted components (blacklist.json can be in the root of react project or fetched from API on first run)

How to test decorated React component with shallow rendering

I am following this tutorial: http://reactkungfu.com/2015/07/approaches-to-testing-react-components-an-overview/
Trying to learn how "shallow rendering" works.
I have a higher order component:
import React from 'react';
function withMUI(ComposedComponent) {
return class withMUI {
render() {
return <ComposedComponent {...this.props}/>;
}
};
}
and a component:
#withMUI
class PlayerProfile extends React.Component {
render() {
const { name, avatar } = this.props;
return (
<div className="player-profile">
<div className='profile-name'>{name}</div>
<div>
<Avatar src={avatar}/>
</div>
</div>
);
}
}
and a test:
describe('PlayerProfile component - testing with shallow rendering', () => {
beforeEach(function() {
let {TestUtils} = React.addons;
this.TestUtils = TestUtils;
this.renderer = TestUtils.createRenderer();
this.renderer.render(<PlayerProfile name='user'
avatar='avatar'/>);
});
it('renders an Avatar', function() {
let result = this.renderer.getRenderOutput();
console.log(result);
expect(result.type).to.equal(PlayerProfile);
});
});
The result variable holds this.renderer.getRenderOutput()
In the tutorial the result.type is tested like:
expect(result.type).toEqual('div');
in my case, if I log the result it is:
LOG: Object{type: function PlayerProfile() {..}, .. }
so I changed my test like:
expect(result.type).toEqual(PlayerProfile)
now it gives me this error:
Assertion Error: expected [Function: PlayerProfile] to equal [Function: withMUI]
So PlayerProfile's type is the higher order function withMUI.
PlayerProfile decorated with withMUI, using shallow rendering, only the PlayerProfile component is rendered and not it's children. So shallow rendering wouldn't work with decorated components I assume.
My question is:
Why in the tutorial result.type is expected to be a div, but in my case isn't.
How can I test a React component decorated with higher order component using shallow rendering?
You can't. First let's slightly desugar the decorator:
let PlayerProfile = withMUI(
class PlayerProfile extends React.Component {
// ...
}
);
withMUI returns a different class, so the PlayerProfile class only exists in withMUI's closure.
This is here's a simplified version:
var withMUI = function(arg){ return null };
var PlayerProfile = withMUI({functionIWantToTest: ...});
You pass the value to the function, it doesn't give it back, you don't have the value.
The solution? Hold a reference to it.
// no decorator here
class PlayerProfile extends React.Component {
// ...
}
Then we can export both the wrapped and unwrapped versions of the component:
// this must be after the class is declared, unfortunately
export default withMUI(PlayerProfile);
export let undecorated = PlayerProfile;
The normal code using this component doesn't change, but your tests will use this:
import {undecorated as PlayerProfile} from '../src/PlayerProfile';
The alternative is to mock the withMUI function to be (x) => x (the identity function). This may cause weird side effects and needs to be done from the testing side, so your tests and source could fall out of sync as decorators are added.
Not using decorators seems like the safe option here.
Use Enzyme to test higher order / decorators with Shallow
with a method called dive()
Follow this link, to see how dive works
https://github.com/airbnb/enzyme/blob/master/docs/api/ShallowWrapper/dive.md
So you can shallow the component with higher order and then dive inside.
In the above example :
const wrapper=shallow(<PlayerProfile name={name} avatar={}/>)
expect(wrapper.find("PlayerProfile").dive().find(".player-profile").length).toBe(1)
Similarly you can access the properties and test it.
You can use 'babel-plugin-remove-decorators' plugin. This solution will let you write your components normally without exporting decorated and un-decorated components.
Install the plugin first, then create a file with the following content, let us call it 'babelTestingHook.js'
require('babel/register')({
'stage': 2,
'optional': [
'es7.classProperties',
'es7.decorators',
// or Whatever configs you have
.....
],
'plugins': ['babel-plugin-remove-decorators:before']
});
and running your tests like below will ignore the decorators and you will be able to test the components normally
mocha ./tests/**/*.spec.js --require ./babelTestingHook.js --recursive
I think the above example is confusing because the decorator concept is used interchangeably with idea of a "higher order component". I generally use them in combination which will make testing/rewire/mocking easier.
I would use decorator to:
Provide props to a child component, generally to bind/listen to a flux store
Where as I would use a higher order component
to bind context in a more declarative way
The problem with rewiring is I don't think you can rewire anything that is applied outside of the exported function/class, which is the case for a decorator.
If you wanted to use a combo of decorators and higher order components you could do something like the following:
//withMui-decorator.jsx
function withMUI(ComposedComponent) {
return class withMUI extends Component {
constructor(props) {
super(props);
this.state = {
store1: ///bind here based on some getter
};
}
render() {
return <ComposedComponent {...this.props} {...this.state} {...this.context} />;
}
};
}
//higher-order.jsx
export default function(ChildComp) {
#withMui //provide store bindings
return class HOC extends Component {
static childContextTypes = {
getAvatar: PropTypes.func
};
getChildContext() {
let {store1} = this.props;
return {
getAvatar: (id) => ({ avatar: store1[id] });
};
}
}
}
//child.js
export default Child extends Component {
static contextTypes = {
getAvatar: PropTypes.func.isRequired
};
handleClick(id, e) {
let {getAvatar} = this.context;
getAvatar(`user_${id}`);
}
render() {
let buttons = [1,2,3].map((id) => {
return <button type="text" onClick={this.handleClick.bind(this, id)}>Click Me</button>
});
return <div>{buttons}</div>;
}
}
//index.jsx
import HOC from './higher-order';
import Child from './child';
let MyComponent = HOC(Child);
React.render(<MyComponent {...anyProps} />, document.body);
Then when you want to test you can easily "rewire" your stores supplied from the decorator because the decorator is inside of the exported higher order component;
//spec.js
import HOC from 'higher-order-component';
import Child from 'child';
describe('rewire the state', () => {
let mockedMuiDecorator = function withMUI(ComposedComponent) {
return class withMUI extends Component {
constructor(props) {
super(props);
this.state = {
store1: ///mock that state here to be passed as props
};
}
render() {
//....
}
}
}
HOC.__Rewire__('withMui', mockedMuiDecorator);
let MyComponent = HOC(Child);
let child = TestUtils.renderIntoDocument(
<MyComponent {...mockedProps} />
);
let childElem = React.findDOMNode(child);
let buttons = childElem.querySelectorAll('button');
it('Should render 3 buttons', () => {
expect(buttons.length).to.equal(3);
});
});
I'm pretty sure this doesn't really answer your original question but I think you are having problems reconciling when to use decorators vs.higher order components.
some good resources are here:
http://jaysoo.ca/2015/06/09/react-contexts-and-dependency-injection/
https://medium.com/#dan_abramov/mixins-are-dead-long-live-higher-order-components-94a0d2f9e750
https://github.com/badsyntax/react-seed/blob/master/app/components/Menu/tests/Menu-test.jsx
https://github.com/Yomguithereal/baobab-react/blob/master/test/suites/higher-order.jsx
In my case decorators are very useful and I dont want to get rid of them (or return wrapped and unwrapped versions) im my application.
The best way to do this in my opinion is to use the babel-plugin-remove-decorators (which can be used to remove them in tests) has Qusai says, but I wrote the pre-processor differently like below:
'use strict';
var babel = require('babel-core');
module.exports = {
process: function(src, filename) {
// Ignore files other than .js, .es, .jsx or .es6
if (!babel.canCompile(filename)) {
return '';
}
if (filename.indexOf('node_modules') === -1) {
return babel.transform(src, {
filename: filename,
plugins: ['babel-plugin-remove-decorators:before']
}).code;
}
return src;
}
};
Take notice of the babel.transform call that im passing the babel-plugin-remove-decorators:before element as an array value, see: https://babeljs.io/docs/usage/options/
To hook this up with Jest (which is what I used), you can do it with settings like below in your package.json:
"jest": {
"rootDir": "./src",
"scriptPreprocessor": "../preprocessor.js",
"unmockedModulePathPatterns": [
"fbjs",
"react"
]
},
Where preprocessor.js is the name of the preprocessor.

Categories

Resources