reactjs base class for all components - reactjs

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.

Related

React Components - No Inheritance, how can pass ref or use HOC for my BaseComponent

I'm building a Web components library where I have a series of components which ideally inherit / takes advantage of BaseComponent. After some reading, inheritance isn't recommended in React and instead I could use forwardRef? or probably higher-order components? The only thing, I'm not very familiar with this concept and couldn't find good examples, tutorial specific for my case. Hopefully someone can tell me how to approach this, what's best-practice?
One of technique I have in mind for the BaseComponent is leveraging IntersectionObserver to trigger animations. As you can imagine, I don't want to put this logic in multiple places. For the sick of having a basic example, below I simply have a click event listener on the BaseComponent:
class Image extends React.Component {
render() {
return (
<div>
<div>
<img className={className} src={src} alt={alt} />
</div>
</div>
);
}
}
export default Image;
// export default withMagic(Image); ??
class BaseComponent extends React.Component {
withMagic() {
}
componentDidMount() {
{/* ref should be <img> DOM element */}
ref.addEventListener("click", this.handleClick);
}
}
export BaseComponent();
Thanks
HoC probably the better solution
// 2. WrappedComponent
export default WrappedComponent => {
// If u want to deal with class
class NewComponent extends React.Component {
//3
handleClick = () => {
// Your Events
}
render () {
//4
return <WrappedComponent {...this.props} handleClick={this.handleClick} />
}
}
// If u want to deal with functional Component
const NewComponent = props => {
const handleClick = () => // your events
return <WrappedComponent {...props} handleClick={handleClick} />
}
return NewComponent
}
How to use ?
import withClick from 'path/withClick'
const A = props => {
return (
//4
<button onClick={props.handleClick}>Click here</button>
)
}
// 1.
export default withClick(A)
How this magic work ?
U are using withClick method by passing A component as a params
A component is named as WrappedComponent inside withClick function
inside the withClick function, u create a new component with your desired handler, logic or even state, then pass them as a props into WrappedComponent
after that, ur current component will have these handler or logic
If u want to pass params, u can use Higher order Function that returns Higher Order Component like
export default (params1, params2, ...paramsn) => WrappedComponent => {
// remain like the same
}

Possible to forward a ref using React.Suspense and React.Lazy?

I wondering if it would be possible to use React 16's lazy, Suspense, createRef and forwardRef features together in order to attach a reference to a dynamically imported component. Does anyone know if this is possible. I tried to follow the example (https://github.com/facebook/react/pull/13446/commits/4295ad8e216e0747a22eac3eed73c66b153270d4#diff-e7eafbb41b012aba463f5a2f8fc00f65R1614), however it doesn't quite seem to work with dynamically imported components.
I have been able to get similar desired behavior by setting custom props in the parent component, passing it down to the child component and then setting the ref to that prop in the child's render function(Example below). My only issue with this approach is that it may not be the cleanest solution and it may be difficult to keep the implementation consistent across a large team. It will also make it easier when attempting to place the ref on the actual component.
// Working (Undesired implementation)
// Parent.js
const LazyClass = lazy(() => { return import('./Child') };
class Parent extends React.Component {
this.childRef = React.createRef();
render() {
return (
<Suspense fallback={<div>Loading</div>}>
<Child forwardRef={this.childRef} />
</Suspense>
);
}
}
// Child.js
class Child extends React.Component {
render() {
return (<div>I am the Child!</div>);
}
}
export default React.forwardRef(({forwardRef, ...props}/*, ref*/) =>
<Child {...props} ref={forwardRef} />
);
//////////////////////////////////////////////////////
// Desired Implementation with React.forwardRef()
//Parent.js
const LazyClass = lazy(() => { return import('./Child') };
class Parent extends React.Component {
this.childRef = React.createRef();
render() {
return (
<Suspense fallback={<div>Loading</div>}>
<Child ref={this.childRef} />
</Suspense>
);
}
}
// Child.js
class Child extends React.Component {
render() {
return (<div>I am the child</div>);
}
}
export default React.forwardRef((props, ref) =>
<Child { ...props } ref={ref} />
);
Setting the ref directly on a Lazily loaded component always returns null. It would be nice if it returned the value from React.createRef().
The "ref as a props" approach is the smaller one but as you said you have to consider prop collision. The approach you linked is still correct. The test only changed slightly:
Update: Not sure if this was a bug previously but lazy now forwards refs automatically. Just be sure you export a component that can hold a ref.
See in action:
https://codesandbox.io/s/v8wmpvqnk0

What type does a component have in react with typescript

I'm new to using typescript. I am passing a component to another component for testing because it has some silly dependencies. The receiving component will have the parameter component listed in its props. Since I'm using typescript, I'll have to give it a type. What type do I need to give it?
I need to avoid including the file because I want to mock it essentially. (Maybe there is a better way to do this, I was planning on replacing it with a function in the tests.)
Here is an example of what I'm talking about:
// Parent Component
import { Thing } from '../thing'
export default class SomeParent extends React.Component<Props, {}> {
public render() {
return (
<Prem
Thing={Thing}
/>
)
}
}
// Prem file
export interface Props {
Thing: What goes here?
}
export default class Prem extends React.Component<Props, {}>{
// Do stuffs
public render() {
return (
<Thing>
<div> herp derp </div>
</Thing>
)
}
}
You can either use the React.ComponentClass<{}> or the React.SFC<{}> (the later for stateless components) classes.
var thing : React.ComponentClass<{}> = ThingComponent
var sfThing : React.SFC<{}> = props => <div></div> {/*Your component's markup*/}
And to create an instance, use:
React.createElement(thing, {props}, [children])
where {props} is key-value paired object representing prop keys and values.

es6 react getting props in child component

I've been migrating one of my apps to ES6 on node/react and I have a question about how props are passed down to children. I read a bunch of posts and some address this while others don't. Basically, what I've seen so far is this:
export default class SomeComponent extends React.Component {
constructor(props) {
super(props);
}
render() {
return (
<div>
{this.props.text} <<< Props used here
</div>
);
}
}
but I've been able to get my component to work with the following:
export default class SomeComponent extends React.Component {
constructor() {
super(); <<< notice no props in parentheses
}
render() {
return (
<div>
{this.props.text} <<< Props used here
</div>
);
}
}
is there a reason why I should pass the props in the parentheses for my constructor and the super call? or can I leave my code the way it is
You don't need to pass props to super unless you want to use this.props in constructor.
https://stackoverflow.com/a/34995257/3238350
You have to pass the props because you are extending from React.Component, otherwise you won't be allowed to access to this.props in the constructor.
It's some kind of composition pattern.

How to extend a React component?

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.

Resources