Export pure functional component without decorators - javascript

I'm trying to export a component without the decorators (connect() in this case)
for unit testing with jest.
So, how could I do this:
import React, { Component } from 'react';
import { connect } from 'react-redux';
export class Header extends Component {
render(){
return <pre>Header</pre>
}
}
export default connect()(Header);
With this component (the export at the beginning doesn't work, it stills exports the connected component)
export let Header = props => {
render(){
return <pre>Header</pre>
}
}
Header = connect()(Header);
export default Header;

Use different variable for your connected component as the following code:
export let Header = props => {
render(){
return <pre>Header</pre>
}
}
let HeaderConnected = connect()(Header);
export default HeaderConnected;
Now you can import your Header freely without using connect()

This can be done without even changing default export:
export let Header = props => {
render(){
return <pre>Header</pre>
}
}
export default connect()(Header);
There may be no need to export original component for connect alone because most well-designed HOCs expose original component:
import Header from '...';
const OriginalHeader = Header.WrappedComponent;

Related

React useContext throws Invalid hook call error

I am trying to pass a value from a context provider to a consumer using useContext and access the value outside of the render function.
My provider looks like so:
export const AppContext = React.createContext();
export class App extends React.Component(){
render(){
<AppContext.Provider value={{ name: 'John' }} ><Main /></AppContext>
}
}
My consumer looks like so
import React, { useContext } from 'react';
import { AppContext } from './App';
export class Main extends React.Component(){
componentDidMount(){
const value = useContext(AppContext);
}
render(){
return (
<div>Main Component</div>
)
}
}
The error is this:
Invalid hook call. Hooks can only be called inside of the body of a function component.
If you want to use hooks they are designed for function components. Like so:
import React, { useContext } from 'react';
import { AppContext } from './App';
const Main = () => {
const value = useContext(AppContext);
return(
<div>Main Component</div>
);
}
If you want to use it in a class based component then just set it as a static contextType in your class and then you can use it with this.context in your component like so:
import React from 'react';
import { AppContext } from './App';
class Main extends React.Component(){
static contextType = AppContext;
componentDidMount(){
const value = this.context;
}
render(){
return (
<div>Main Component</div>
)
}
}
Edit:
Remove your context from your app component and place it in its own component. I think you are receiving conflicts in your exporting of your context.
so your app component should look like:
import React from "react";
import Context from "./Context";
import Main from "./Main";
class App extends React.Component {
render() {
return (
<Context>
<Main />
</Context>
);
}
}
export default App;
Your main component should be like:
import React from "react";
import { AppContext } from "./Context";
class Main extends React.Component {
static contextType = AppContext;
render() {
return <div>{this.context.name}</div>;
}
}
export default Main;
and your context component should be like:
import React from "react";
export const AppContext = React.createContext();
class Context extends React.Component {
state = {
name: "John"
};
//Now you can place all of your logic here
//instead of cluttering your app component
//using this components state as your context value
//allows you to easily write funcitons to change
//your context just using the native setState
//you can also place functions in your context value
//to call from anywhere in your app
render() {
return (
<AppContext.Provider value={this.state}>
{this.props.children}
</AppContext.Provider>
);
}
}
export default Context;
Here is a sandbox to show you it working CodSandbox
You get the above error because Hooks are meant to be used inside functional components and not class component whereas you try to use it within componentDidMount of Main component which is a class component
You can rewrite your code for Main component using useContext hook like
import React, { useContext } from 'react';
import { AppContext } from './App';
export const Main =() =>{
const value = useContext(AppContext);
return (
<div>Main Component</div>
)
}
or use Context in a different way with class like
import React from 'react';
import { AppContext } from './App';
class Main extends React.Component {
componentDidMount(){
const value = this.context;
// use value here. Also if you want to use context elsewhere in class
// you can use if from this.context
}
render(){
return (
<div>Main Component</div>
)
}
}
Main.contextType = AppContext;
export { Main };
Hooks only work with stateless components. You are trying to use it in class component.
Here is the content for Main.js file. Uncomment the commented part if you want to use class-based component instead of the functional one.
import React from "react";
import { AppContext } from "./App";
/** UNCOMMENT TO USE REACT CLASS COMPONENT */
// class Main extends React.Component() {
// render() {
// return (
// <AppContext.Consumer>
// {value => <div>It's Main component. Context value is ${value.name}</div>}
// </AppContext.Consumer>
// );
// }
// }
const Main = () => {
const value = React.useContext(AppContext);
return <div>It's Main component. Context value is ${value.name}</div>;
};
export default Main;
Here is the content for App.js file. Uncomment the commented part if you want to use class-based component instead of the functional one.
import React from "react";
import ReactDOM from "react-dom";
import Main from "./Main";
export const AppContext = React.createContext();
/** UNCOMMENT TO USE REACT CLASS COMPONENT */
// export class App extends React.Component() {
// render() {
// return (
// <AppContext.Provider value={{ name: "John" }}>
// <Main />
// </AppContext.Provider>
// );
// }
// }
const App = () => (
<AppContext.Provider value={{ name: "John" }}>
<Main />
</AppContext.Provider>
);
const rootElement = document.getElementById("root");
ReactDOM.render(<App />, rootElement);
React Hooks were implemented directly for the functional components in order to give them the possibility to become stateful. Class-based components were stateful all the time, so you have to use their own state API.
Working demo is available here.

How to use experimental decorators and reconcile types in TypeScript?

I have the following basic component
import * as React from 'react';
import { withRouter, RouteComponentProps } from 'react-router-dom';
export interface Props { };
#withRouter
export default class Movies extends React.PureComponent<Props> {
goBack = () => {
this.props.history.goBack();
};
public render() {
return (
<div>
<button onClick={this.goBack}>Back</button>
</div>
);
}
}
I would expect withRouter to inject specific props into my component. However, when referencing this.props.history, I get this:
What is the appropriate way to use decorators which will add type definitions?
I do it this way:
import * as React from 'react';
import { withRouter, RouteComponentProps } from 'react-router-dom';
export interface OwnProps { };
type Props = OwnProps & RouteComponentProps<{}>;
#withRouter
export default class Movies extends React.PureComponent<Props> {
goBack = () => {
this.props.history.goBack();
};
public render() {
return (
<div>
<button onClick={this.goBack}>Back</button>
</div>
);
}
}

React - How to export a pure stateless component

How can I export a stateless pure dumb component?
If I use class this works:
import React, { Component } from 'react';
export default class Header extends Component {
render(){
return <pre>Header</pre>
}
}
However if I use a pure function I cannot get it to work.
import React, { Component } from 'react';
export default const Header = () => {
return <pre>Header</pre>
}
Am I missing something basic?
ES6 doesn't allow export default const. You must declare the constant first then export it:
const Header = () => {
return <pre>Header</pre>
};
export default Header;
This constraint exists to avoid writting export default a, b, c; that is forbidden: only one variable can be exported as default
Just as a side note. You could technically export default without declaring a variable first.
export default () => (
<pre>Header</pre>
)
you can do it in two ways
const ComponentA = props => {
return <div>{props.header}</div>;
};
export default ComponentA;
2)
export const ComponentA = props => {
return <div>{props.header}</div>;
};
if we use default to export then we import like this
import ComponentA from '../shared/componentA'
if we don't use default to export then we import like this
import { ComponentA } from '../shared/componentA'
You can also use a function declaration instead of assignment:
export default function Header() {
return <pre>Header</pre>
}
In your example, you already use curly brackets and return so this is apparently matching with your needs with no compromise.

react-redux nested component not ReactClass in parent?

I am a bit new to react and redux, but made quite a lot of progress.
I am using redux connect to map state to props. Was working like charm, until I got this situation:
Parent component is using mapStateToProps, and here is the source code for it:
import React from 'react';
import { connect } from 'react-redux'
import { NestedComponent } from './NestedComponent'
class ParentElement extends React.Component {
render() {
return (
<div className="App">
<NestedComponent/>
</div>
);
}
}
const mapStateToProps = state => {
const { questions } = state
return {
questions
}
}
ParentElement.contextTypes = {
router: React.PropTypes.object.isRequired
}
export default connect(mapStateToProps)(ParentElement)
Here is the code for the nested element:
import React from 'react';
import { connect } from 'react-redux'
class NestedComponent extends React.Component {
render() {
return (
<div> I am nested</div>
);
}
};
const mapStateToProps = state => {
const { questions } = state
return {
questions
}
}
NestedComponent.contextTypes = {
router: React.PropTypes.object.isRequired
}
export default connect(mapStateToProps)(NestedComponent)
When I try to show the parent element, I get this error:
Warning: React.createElement: type should not be null, undefined,
boolean, or number. It should be a string (for DOM elements) or a
ReactClass (for composite components). Check the render method of
ParentElement.
What am I missing / doing wrong?
Import header file like this:-
import NestedComponent from './NestedComponent'
Without braces.
When a class is exported as a default then it is imported without braces because there is only one default class in a file but, when the class is exported without default you have to import it with braces because there can be more than one class with export keywords in a file (es6 conventions).

Decorate imported React component with higher order component

I know how to decorate a component before exporting it like this:
export default ButtonDecorator(MainButton)
But if I try to make a index to import it and decorate it differently for some cases. It wont work.
Here is the example of the index:
import MainButton from './main/main_button'
import BackButton from './back/back_button'
import { ButtonDecorator, LinkDecorator } from 'decorators'
export {
ButtonDecorator(MainButton) as MainButton,
LinkDecorator(MainButton) as MainHrefButton,
BackButton
}
And the higher order component:
import React, { Component } from 'react'
let Btn = InnerComponent => {
class NewBtn extends Component {
constructor(props) {
super(props)
}
render() {
return (
<button onClick={this.props.onClick}>
<InnerComponent disabled={this.props.disabled} />
</button>
)
}
}
return NewBtn
}
export default Btn
What is the right way to do this?
As far as I know export requires a name (variable), and not a function invocation. Try this:
const ButtonDecoratedMainButton = ButtonDecorator(MainButton);
const LinkDecoratedMainButton = LinkDecorator(MainButton);
export {
DecoratedMainButton as MainButton,
LinkDecoratedMainButton as MainHrefButton,
BackButton
}

Categories