How do I properly extend React Bootstrap components? - javascript

Say I have a badge component I want to add sizing for. This is how I currently do it:
import React from 'react';
import Badge from 'react-bootstrap/Badge';
import classNames from 'classnames';
const BadgeExtended = props => {
const {className, size, ...attr} = props;
const classes = classNames(
className,
size && `badge-${size}`
);
return <Badge className={classes} {...attr}>{props.children}</Badge>;
};
export default BadgeExtended;
which works OK. Is that a correct way to do it? Is there a way to extend the original component so that I dind't have to import an extended one, using react-bootstrap/Badge instead?

I think the way you came up with is the right way. If you look at the repository (here), the Badge component is functional, not a class, so there's no way to use the extend keyword.
Making an HOC seems like the best way to achieve what you're doing.

Related

Persist data between two pages with Next.js

I would like to refactor my Next.js webapp to have different pages handle different screens. Currently, I have this component holding several states to know in which screen I'm in. In the jsx section, I'm using {value && ... } to render the right component.
But I feel this is not good design, and won't be maintainable when adding more and more screens.
I would also like to avoid Redux as it is overkill for my project.
I was thinking about persisting data in cookies so I can retrieve them with getInitialProps in every component when rendering a new page, but is there a more elegant way?
I've read about tweaking the _app.js but I'm not sure to understand the consequences of doing so, and how it could help me..
Any suggestion?
When multiple of your pages need to make use of same data, you can make use of Context to store the result. It a good way to make a centralized storage without using complex and more self sufficient libraries like redux
You can implement context inside of _app.js file which must reside inside your root folder. This way next.js treats it as a root wrapper and you would just need to use 1 instance of Context
contexts/appContext
import React from 'react';
const AppContext = React.createContext();
export const AppProvider = AppContext.Provider;
export const AppConsumer = AppContext.Consumer;
export default AppContext;
_app.js
import React from 'react'
import App from 'next/app'
import AppProvider from '../contexts/appContext';
class MyApp extends App {
state={
data:[]
}
render() {
const { Component, pageProps } = this.props;
// You can implement logic in this component to fetch data and update state
return (
<div>
<AppProvider value={this.state.data}> // pass on value to context
<Component {...pageProps} />
</AppProvider>
</div>
)
}
}
export default MyApp
Now further each component can make use of context value by using AppConsumer or using useContext if you use hooks
Please read more about how to use Context here

having access to a parent state from component's props.children:

I am making a container for a d3 line graph that I'm going to create, my format so far is this:
import React from "react";
import AnalyticPads from "../AnalyticPad/AnalyticPad";
import Pad from "../AnalyticPad/Pad";
import MainContent from "../AnalyticPad/MainContent";
import Extention from "../AnalyticPad/Extention";
const GraphPad = () => {
return (
<AnalyticPads properties={{height: "200px"}}>
<Pad>
<MainContent>
</MainContent>
<Extention>
</Extention>
</Pad>
</AnalyticPads>
)
}
export default GraphPad;
And my "AnalyticsPad" looks like this:
import React from "react";
const AnalyticPads = (props) => {
return (
<div className="analytic-pad-container">
{props.children}
</div>
)
}
export default AnalyticPads;
What I want is that there will be a grid of "Pads" and I want this "AnalyticsPad" to provide default styles for each pad, for example if I want each pad to have a height of 200px I set it in this wrapper and then for any individual pad that I want to differ from the default I can overide it.
The "MainContent" component is where the line graph will be and any extra information will be put inside the "Extention" which will render if a button is pressed.
Throughout my react app I keep using the context api to provide the data to the children components, but I know ( or think ) it is bad practice to do this, from what I understand context should only be used for providing data to global components.
What is best practice?
please don't answer with a solution for class components as I have never used them before as I am new to react.

How to share a library globally in React?

For example, there's styled-components library, what if i want to use it in 10 components, i would have to import it 10 times? Or HOC is the only way to deal with that?
I'm doing this in my ExampleComponent:
import styled from 'styled-components`;
Then i can use it, i need an example with HOC approach or something better.
... i would have to import it 10 times?
Only if the components are all defined in their own modules, and they all need to use styled-components. You have to import once per module, not once per component. There's no requirement that each component be written in its own module, doing so (or not) is a matter of style.
As Dan Abramov said:
I still get surprised that “one function per file” is clearly unnecessary but “one component per file” is somehow a common practice!
In any case, don't worry: You only have one copy of the library. The import is just binding the modules together, not actually copying the module you import from into your module.
In case you want a HOC. You can do something like:
import styled from 'styled-components`;
const Wrapper = styled.section`
padding: 4em;
background: papayawhip;
`;
const withWrapper = component => {
return <Wrapper>{component}</Wrapper>
}
export default withWrapper;
And then use it as:
const Home = () => {
return <h1>Home</h1>
}
export default withWrapper(Home);
Hope this will help you.

How do I extend React components already in use?

This applies to just about any scenario along these lines:
I have a React app that uses React Router <Link>s, and they are scattered throughout my app. I want to extend or prototype the <Link> component so that there is a new attribute when they are rendered. (I just want to add an attribute to all Link tags.)
How can I update the Link component that is being used throughout the app, to have a new attribute [without creating a new component, like <CustomAttributeLink>]?
Thanks
The best way to do this is by cloning the element, you should use React.cloneElement. To make the component usable everywhere, just create a new component using it, and export it.
import React from "react";
import {Link} from "react-router";
const CustomLinkAttribute = React.cloneElement(Link, {
newProp: "New prop val here"
});
export default CustomLinkAttribute;
You can clone the element and add the extra props using React.cloneElement e.g:
var clonedElementWithExtraProps = React.cloneElement(
MainElement,
{ newProp: "This is a new prop" }
);
Return clonedElementWithExtraProps;

Why HOC are applied during exporting of component in place of importing it

My basic understading is that HOC like connect (for connecting with redux store) and other HOC's are applied to a component while exporting it.
Like this
import React, { Component } from 'react';
import './App.css';
import myHoc from './myHoc/index';
class App extends Component {
render() {
return (
<div className="App">
</div>);
}
}
export default myHoc({})(App);
Where as a better thing would be to apply HOC during import as it would make it easier to make reusable component. The same component can pick up props from store or from props and that would be the responsibility of the parent component to check what to give which HOC to apply on the component.
I know we can use container components which takes the component and render children but that just adds code in the JSX (wont look good if there are many container components)
though we can do it like this
import React, { Component } from 'react';
import './App.css';
import myHoc from './myHoc/index';
import AppChild from './AppChild';
const NewAppChild = myHoc({}, ()=> {
})(AppChild);
class App extends Component {
state = {
count: 1,
};
reRender = () => {
this.setState({count: this.state.count + 1});
};
render() {
return (
<div className="App">
<NewAppChild handleClick={this.reRender} count={this.state.count}/>
</div>
);
}
}
export default App;
What my question is that, is there something better that can handle this kind of situations where I want to apply my HOC on import that is each many container components can import it and they can apply different HOCs depending on the needs.
There is no single concrete reason for this design choice - as you have already seen you can invoke your HOC wherever you use the component - but I see at least 1 advantage: configuration & component reuse.
In your example, myHoc takes no parameters or configuration so this doesn't necessarily apply, but imagine instead that you are invoking connect from redux.
In most use cases, connect accepts 2 configuration functions -
mapStateToProps & mapDispatchToProps - that define the behaviour. If you define those within MyComponent then any consuming component can import MyComponent from 'MyComponent' and start using it.
If you instead rely on the parent component to call connect() then you are forcing every consumer to re-implement the configuration of connect as well. That may mean many instances of duplicated configuration and adds to the complexity for consuming components.
That being said, there are certainly cases where you might want this behaviour - for example, if you wanted to connect the same component to different state definitions. Ultimately you need to pick the best pattern to support what you need from the component.

Categories

Resources