The current way to 'export default' using decorators in NextJS - reactjs

I have a NextJS app that I'm upgrading to the latest version to take advantage of SWC (compared to older versions that used Babel).
As it turns out NextJS's implementation isn't exactly the same since Babel was supporting the legacy decorator proposal. The comments from NextJS team haven't been helpful either, so I turn to StackOverflow as I can't seem to get the right syntax:
// Old Code
#connect( mapStateToProps, { followUser, unfollowUser } )
export default class FollowCTA extends Component {
}
According to the latest decorator proposal, "Class decorators come after export and default"
// New Code as per docs
export default
#connect( mapStateToProps, { followUser, unfollowUser } )
class FollowCTA extends Component {
}
The above code however throws the following error where that component was being used:
Unhandled Runtime Error
ReferenceError: FollowCTA is not defined
What is the right way to continue using decorators in NextJS?

Related

Is there any way to fix errors caused by Bootstrap when upgrading to React 18

I am following a guide to upgrade to React 18. After completing the upgrade I am seeing errors on certain pages in my app.
ReactDOM.unstable_renderSubtreeIntoContainer() is no longer supported in React 18.
I am not using the unstable_renderSubtreeIntoContainer() function anywhere in my app, but when I look closer at what is causing these errors it seems to be caused my Bootstrap components.
Is there anyway to update this to remove the errors?
I ran into the same problem with react-bootstrap#v0.33.1 specifically when using OverlayTrigger component after upgrading to React 18. The warning message suggests to migrate to using portals. So I implemented a CustomOverlayTrigger component that leverages portals and referred to React's portal documentation to do so. Note that this solution is for Bootstrap 3 usage of OverlayTrigger (react-bootstrap v0.33.1). It seems later versions of react-bootstrap got rid of using ReactDOM.unstable_renderSubtreeIntoContainer. If you are not in a position to migrate to later versions (like I am), this solution will help for this use case. I have not check thoroughly if other components use the deprecated method, but the approach might be the same.
First of all, I copied the original source of the OverlayTrigger component code located here. You will need to clean up the imports and include into your code the utils function createChainedFunction located here.
I then created a portal wrapper based off React's documentation that looks like this:
import React from 'react';
import ReactDOM from 'react-dom';
import PropTypes from 'prop-types';
const tooltipRoot = document.getElementById('tooltip-root');
class PortalWrapper extends React.Component {
constructor(props) {
super(props);
this.el = document.createElement('div');
}
componentDidMount() {
tooltipRoot.appendChild(this.el);
}
componentWillUnmount() {
tooltipRoot.removeChild(this.el);
}
render() {
// eslint-disable-next-line react/destructuring-assignment
return ReactDOM.createPortal(this.props.children, this.el);
}
}
PortalWrapper.propTypes = {
children: PropTypes.oneOfType([
PropTypes.arrayOf(PropTypes.node),
PropTypes.node,
]).isRequired,
};
export default PortalWrapper;
At the top, you can see the line const tooltipRoot = document.getElementById('tooltip-root');, I simply added in my index.html a div next to the react app's root div that will server as the anchor for my portal.
Then, back in the CustomOverlayTrigger component copied from react-bootstrap, I edited it in the follwing manner:
Remove all references to this._overlay and this._mountNode because the PortalWrapper now manages the mounting/unmounting. So I deleted componentDidMount(), componentDidUpdate(), componentWillUnmount() and renderOverlay()
I modified makeOverlay so that its result is wrapped by PortalWrapper so it became the following:
makeOverlay(overlay, props) {
return (
<PortalWrapper>
<Overlay
{...props}
show={this.state.show}
onHide={this.handleHide}
target={this}
>
{overlay}
</Overlay>
</PortalWrapper>
);
}
Finally, I changed the render method's return statement to become:
return (<>
{cloneElement(child, triggerProps)}
{this.makeOverlay(overlay, props)}
</>);
After this, I simply had to replace all my invocations to OverlayTrigger with CustomOverlayTrigger and I had the same result without the warning message.

React Developer Tools shows all components as "Anonymous"

I've installed the React Developer Tools extension on Google Chrome to debug a React application written in TypeScript, but once I start debugging the application and open the "Components" window, all components are shown as "Anonymous".
Granted, the application uses mostly function components.
Does anyone know if there is a way to get React Developer Tools to show component names in its component tree?
This happens when you define your components like so:
// Default arrow function export
export default () => {
// ...
}
// Default function export
export default function() {
// ...
}
You can replace with the following to fix the issue:
const CustomComponent = () => {
// ...
}
export default CustomComponent;
// or
export default function YourComponent() {
// ...
}
If you're using an exported anonymous functional component or a memo'ed component; it would be shown as Anonymous
Refer this - https://github.com/facebook/react/issues/17876
Or try solutions mentioned here - React Dev tools show my Component as Unknown

react-intl v4 upgrade `IntlProvider` not found preventing component from displaying when using `injectIntl` wrapper

I am trying to upgrade from react-intl v 2.3.0 to 4.5.1. I have run into a few issues around the injectIntl HOC since the upgrade. I have a shared component library within a monorepo that is imported into different features (combination of imported shared and local unique components in a separate part of the repo). However, my IntlProvider lives in my shared components library and we import a component called AppShell that includes any needed providers (including intl) that wraps the top level of every feature.
I followed advice found on an issue on the formatjs repo and deleted the react-intl version from my shared component library. This solved most of my issues with a few caveats.
Note, all examples below are using a feature folder with react-intl version 4.5.1, and a shared component library having no version of react-intl installed
Fix attempt 1
If I try to use the injectIntl HOC from react-intl the component will not render on the page and I get the following errors:
code example (AppShell wrapper component lives in the top level ReactDOM.render function):
import { FormattedMessage, injectIntl } from 'react-intl';
// shared component library
import { Alert } from '#shared/components/alert';
// component custom to feature, also can contain shared components
import WorkspaceListContainer from './workspaceListContainer';
import messages from './messages';
export class AppLifecycleMenu extends Component {
render() {
const deleteAlertTitle = this.props.intl.formatMessage(
messages.deleteAlertTitle,
{ alertName: name }
);
return (
<Fragment>
<WorkspaceListContainer />
<Alert
title={deleteAlertTitle}
okButtonText={<FormattedMessage {...messages.deleteOkButton} />}
cancelButtonText={<FormattedMessage {...messages.deleteCancelButton} />}
/>
</Fragment>
);
}
}
export default injectIntl(AppLifecycleMenu);
Note, in a lot of our shared components, intl messages can be passed in as props and some components are wrapped in their own injectIntl HOC to intl default versions of props (like a default button label).
Fix attempt 2
However, if I import an injectIntl helper we have in our shared resource library, the component renders but still displays an error message:
injectIntlHelper code:
// only apply intl in non-test environments
export default Component => process.env.NODE_ENV === TEST_ENV ? Component : injectIntl(Component);
code example (AppShell wrapper component lives in the top level ReactDOM.render function):
import { FormattedMessage, injectIntl } from 'react-intl';
// shared component library
import { Alert } from '#shared/components/alert';
import injectIntl from '#shared/utilities/injectIntl';
// component custom to feature, also can contain shared components
import WorkspaceListContainer from './workspaceListContainer';
import messages from './messages';
export class DeleteWorkspaceAlert extends Component {
render() {
const deleteAlertTitle = this.props.intl.formatMessage(
messages.deleteAlertTitle,
{ alertName: name }
);
return (
<Fragment>
<WorkspaceListContainer />
<Alert
title={deleteAlertTitle}
okButtonText={<FormattedMessage {...messages.deleteOkButton} />}
cancelButtonText={<FormattedMessage {...messages.deleteCancelButton} />}
/>
</Fragment>
);
}
}
export default injectIntl(AppLifecycleMenu);
component tree with intlprovider:
Error message:
Fix attempt 3
I also tried only having react-intl installed in the shared resource library but that results in this error message:
Environment
I have tried going down versions in react-intl, but this issue persists back down into the 3.0 and up versions
I've been trying different combinations and looking online for a few days, are there any other changes I can make to get rid of these errors? Is anything jumping out as something I am missing adding or updating between versions?
For anyone who comes across this, my issue was solved by following/modifying the fix from the issue: https://github.com/formatjs/formatjs/issues/1620
Unlike the issue linked above, my projects IntlProvider lives in the shared resource library and is exported to wrap components. Therefore I deleted the react-intl dependency from all folders other than my shared resource library and exported any needed components from there.
So my imports in my non-shared components folder would look like
import injectIntl, { FormattedMessage, FormattedDate } from '#shared/utils/intl';
with component(s) in my shared library that have files that look like this
import { FormattedMessage } from 'react-intl;
export FormattedMessage;
Relevant quote from the issue:
I think the reason [having multiple dependencies] worked with older version is old React context API. New React context API rely on same reference. You project instantiates react context from different node_modules thus contexts are not same reference. This also means you are bundling react-intl twice in your application.

"Uncaught TypeError: Super expression must either be null or a function" - based on my code, why am I getting this error?

I'm trying to setup a modal with React. I'm using code that is more or less taken from their docs. I'm not sure why I'm getting this error: "Uncaught TypeError: Super expression must either be null or a function." I've poked around and I've noticed this error usually appears when people forget to capitalize "Component" when setting up a class - class Modal extends React.Component - but that doesn't appear to be my problem.
I have React 16.6 and React-dom 16.6 as dependencies. Perhaps I don't have the most updated version of React and it's causing issues? Perhaps there is something else going on in my other files that reference this Modal that is causing the problem? Either way, I don't have a clear path to fixing this error because I don't know what is wrong, based on the error messaging in Chrome dev tools. My terminal isn't providing any error messaging either. I'm lost.
import React from "react";
import { createPortal } from "react-dom";
const modalRoot = document.getElementById("modal");
class Modal extends React.Compoment {
constructor(props) {
super(props);
this.el = document.createElement("div");
}
componentDidMount() {
modalRoot.appendChild(this.el);
}
componentWillUnmount() {
modalRoot.removeChild(this.el);
}
render() {
return createPortal(this.props.children, this.el);
}
}
export default Modal;
The Modal should automatically render as soon as the application is started in the browser (yes, I'm aware that Modals can be pretty annoying for users but I'm still trying to learn how to make one on React)
class Modal extends React.Compoment, there is your error it should be Component with n

exporting a react component as npm package

Says I want to share a simple react component in npm package, to let others use it, must I do this?
import SomeComponent from './components/SomeComponent';
module.exports = {
SomeComponent: SomeComponent
}
reference
https://github.com/alanbsmith/npm-example-module/blob/master/lib/index.js
In package.json of the project you can see the main file of the package is build/index.js, but why the author don't just export components/SomeComponent, but instead created a wrapper? I understand he use babel, it's to support the code for legacy browser but why use module.exports in this case?
If you're using es6 imports, you should use es6 exports as well for consistency.
import SomeComponent from './components/SomeComponent'
export { SomeComponent }
And in your package.json set the main field to the file after build. such as
{
"main": "lib/index.js"
}
Then people can import it like this:
import { SomeComponent } from 'my-lib'
The example you're following looks really outdated, though, and doesn't follow a lot of good practices. Here's a more modern-looking solution that you'll probably have less trouble with.

Resources