How to extend a React component? - reactjs

Let's say there's a React component that I like, but want to modify. For this example, we'll use Material UI's LinearProgress. I want to make a clickable seek bar out of it.
class SeekBar extends LinearProgress {
componentDidMount() {
super.componentDidMount();
console.log('my stuff here');
}
}
But I feel like I might be very limited as to what I can do as far as changing what render returns. Maybe I'm going about this all wrong, though. If I like a particular React component such as a Material UI component, what is a good, reusable way to customize its look and functionality and make it my own?

In the ES6/JSX/React world, you can extend component behaviors and values in many ways. I'd recommend one of the following, considering that you use Material UI.
First case:
You have two components that extend the Component from React:
class ExampleComponent extends React.Component {
render () {
return(
<div>
<button {...this.props}>
Click me!
</button>
</div>
)
}
}
class RenderComponent extends React.Component {
clickHandler () {
console.log('Click fired!')
}
render () {
return(
<ExampleComponent onClick={this.clickHandler.bind(this)} />
)
}
}
In that example, onClick is passed via props inside the rendered ExampleComponent. Example here.
Second case:
This is similar on how Material UI extends their own components:
class ExampleComponent extends React.Component {
clickHandler () {
console.log('Click fired!')
}
}
class RenderComponent extends ExampleComponent {
render () {
return(
<div>
<button onClick={this.clickHandler.bind(this)}>
Click me!
</button>
</div>
)
}
}
In this example, you have one component that extends Component from React but only has event methods. Then, you extend this component and render your own with the extended behavior. Here is a live example.
Hope it helps!

One way is:
export default class Seekbar extends React.Component{
// perform any modification
render(){
return <LinearProgress ...changes/>
}
}
Another way is a Higher Order Component, HOC. Here's a great blog with more info: http://natpryce.com/articles/000814.html
I think with Material-UI your best bet would be to wrap it and make any modifications you'd like. Unfortunately this project is very tightly coupled thanks to things like inline styles and their thememanager so taking a single component out may be difficult. HOC would be better for things like sharing some smart functionality across components, like passing a "theme" prop automatically to components.

Related

making a new react component want to add code not DOM

I'm relearning react, I've done tons and tons of angular I ran the following command :
generate-react component carousel
(I'm using generate-react-cli)
this works well and generates the following .tsx file (plus test, lazy-loading and style files, which I am omitting) :
import React from 'react';
import styles from './carousel.module.scss';
const carousel: React.FC = () => (
<div className={styles.carousel}>
carousel Component
</div>
);
export default carousel;
this loads fine in my app but now I want to add actual code, functions.
I can't seem to do this.
I've broken open this const like so :
const carousel: React.FC = () => {
constructor(props) {
super(props);
}
componentDidMount() {
}
return (
<div className={styles.carousel}>
carousel Component
</div>
);
}
I assume this is where the constructor and so on go,
however in this area the constructor name and ect. is not allowed.
so I tried changing the type to React.Component
however at that point I get an error directly on the const name stating :
TS2740: Type '() => Element' is missing the following properties from
type 'Component{}, {}, any>': context, setState, forceUpdate, render,
and 3 more.
I'm really shooting in the dark here and reacts docs are antithetical to examples. they refuse to help with any of this.
I want this newer, shorthand syntax but I also want to be able to use lifecycles and functions.
As it turns out the correc syntax for this is as follows :
class carousel extends React.Component {
componentDidMount() {
console.log('hello');
}
render() {
return (
<div className={styles.carousel}>
carousel Component
</div>
);
}
}

How to make components explicit in what props they accept?

Is there any known practice to make a React component constructor explicit in what arguments it accepts?
Looking at a component like below, I cannot immediately tell what do I need to pass to the constructor to initialize the component.
Instead, I need to search for specific props in the component body or check how has the component been initialized previously (if it has).
class MenuItem extends React.Component {
constructor(props) {
super(props)
}
render() {
return <div name={this.props.name}></div>
}
}
I would need something like:
class MenuItem extends React.Component {
constructor(name) {...
You can explicitly show what props your component accepts, including their types.
MenuItem.propTypes = {
name: PropTypes.string // optional string
};
You can even define them as required:
MenuItem.propTypes = {
name: PropTypes.string.isRequired // required string
};
Be sure to import the library:
import PropTypes from 'prop-types';
More information in the react docs
There is several options to type your props.
The first one is React PropTypes, it works like this:
import PropTypes from 'prop-types';
class MenuItem extends React.Component {
constructor(props) {
super(props)
}
render() {
return <div name={this.props.name}></div>
}
}
MenuItem.propTypes = {
name: PropTypes.string.isRequired
}
You just have to write the name and types of your props inside an attribute "propTypes" of your component class.
React PropTypes is a good solution when you want to type your component props.
https://reactjs.org/docs/typechecking-with-proptypes.html#proptypes
There is another solution, a Javascript Flavor like Typescript.
After setting up Typescript on your React app, you can use Typescript with your component like this:
interface IMenuItem {
name: string;
}
class MenuItem extends React.Component<IMenuItem> {
constructor(props) {
super(props)
}
render() {
return <div name={this.props.name}></div>
}
}
The advantage with Typescript is that you can type every variable, every state, every props, everything !
However, this solution is expensive in time because you have to learn well Typescript and you have to type correctly every piece of your app.
https://www.typescriptlang.org/docs/handbook/typescript-in-5-minutes.html
Finally, if you just want your IDE to tells you what props you can use without typing them, you can just use object destructuring on your props with a Functional Component like this:
const MenuItem = ({name}) => (
<div>{name}</div>
)
Hope this will help !
If your concerns are mainly about visibility you may be better off only writing functional components which is possible since hooks do not require any class components anymore:
const MenuItem = ({name}) => (
<div>{name}</div>
)
Buy destructuring the props in the argument list you can easily see which props are being used immediately just by looking at the function definition. From my experience writing 100's of components, functional components are usually more concise, easier to grasp and reduce boilerplate code.
Apart from that javascript is still a scripting language which means anything can be passed at runtime. PropTypes or using typescript may help you with auto-completion and type checking.

How to use react-alert with React component that is defined as ES6 class

I am trying to use https://www.npmjs.com/package/react-alert in my project, but the examples are porvided for the React components that are declared as functions but my React components are declared as classes like:
class ConnectedOrderForm extends Component {
//const statusAlert = useAlert();
constructor(props) {
super(props);
}
render() {
return (
<div>Test</div>
)
}
}
const OrderForm = withRouter(connect(mapStateToProps, mapDispatchToProps)(ConnectedOrderForm));
export default OrderForm;
When I am trying to call useAlert().show('OK'); from class event, I am getting:
Invalid hook call. Hooks can only be called inside of the body of a function component. This could happen for one of the following reasons:
1. You might have mismatching versions of React and the renderer (such as React DOM)
2. You might be breaking the Rules of Hooks
3. You might have more than one copy of React in the same app
So, how can I use this react-alert hook from ES6 class component?
The solution was to use either export code:
const OrderForm = withRouter(connect(mapStateToProps, mapDispatchToProps)(ConnectedOrderForm));
export default withAlert()(OrderForm);
const OrderForm = withAlert()(withRouter(connect(mapStateToProps, mapDispatchToProps)(ConnectedOrderForm)));
export default OrderForm;
And call:
this.props.alert.show()
Based on the docs it would seem that you should not be using the useAlert hook but rather the withAlert HOC. Hooks can only be used in functional components, and since you want to use a class, you would need to the withAlert HOC.
Here is an example of that that would look like
import { withAlert } from 'react-alert'
class App extends React.Component {
render() {
const alert = this.props.alert;
return (
<button
onClick={() => {
alert.show('Oh look, an alert!')
}}
>
Show Alert
</button>
);
}
}
export default withAlert()(App)
Did you try this one?
import {alert} from 'react-alert';
alert.show('Some message', {
... options
})
UPD. Sorry, this is wrong answer, Please take a look here:
https://github.com/schiehll/react-alert/issues/116

Container and Component of the same Class

I'm new to React and Redux,
I'm trying to use a Class as a Redux Container (so the state is managed by reducer) and also a classic Component with another instance (no link to reducer)
So basically something like this:
class BaseLogo extends Component {
constructor(props){
super(props);
}
render(){
let link = this.props.linkLogo || this.props.link;
return (
<Link className="btn btn-primary" to={link}>
{this.props.logoName}
</Link>
);
}
}
function mapStateToProps(state){
return {
linkLogo: state.linkLogo
};
}
let Logo = connect(mapStateToProps)(BaseLogo);
export {Logo, BaseLogo as Button};
So I wanna use Logo as a Container, and Button as a Component (no reducer) with a specified props.
But as the way Redux bind it, whenever I create an instance of class Logo, there will be a reducer.
Is there somehow I can isolate the connect()() from the class itself ?
Thank you.
I found the error, it dues to a bad implementation in the code. Thank you all

reactjs base class for all components

I want to have a base component that all my higher order components extends it. Something along the lines of
<BaseComponent>
<App1>
<... Other Components>
</App1>
</BaseComponent>
where BaseComponent contains things such as
class BaseComponent extends React.Component {
render() {
return (
<Wrapper>
{this.props.children}
<ModalContainer>
</ModalContainer>
<Wrapper>);
}
}
The ultimate goal is to be able to, in any of the Apps pass lets say "message = 'Error'" and a modal dialog will display saying "Error" without having to put the modal component in every single app.
Is this possible? or am I in the realm of unicorns. I read a little about higher order compositions but at first glance, it doesn't seem like that's what i'm looking for.
Component composition via higher-order components is a way to do something like subclassing but with composition instead of inheritance. For example:
function wrapInBaseComponent(Component) {
// Return a new component that renders `Component`
// with all the same props, but also renders some
// other stuff
return (props) => {
const { message, ...otherProps } = props;
return (
<Wrapper>
<Component {...otherProps} />
<ModalContainer message={message}>
</ModalContainer>
</Wrapper>
);
};
}
Then you'd do something like this:
class MyComponent extends React.Component {
// ...
}
const WrappedComponent = wrapInBaseComponent(MyComponent);
Or, if you have ES7 decorators enabled, you can use it as a decorator:
#wrapInBaseComponent
class MyComponent extends React.Component {
// ...
}
This is how things like react-redux and react-dnd work; you don't inherit from ReactReduxBaseComponent or anything like that, you compose your component in a higher-order component that renders it, but adds additional functionality.

Resources