How do I extend React components already in use? - javascript

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;

Related

Can not read history in class component

I am trying to navigate to other page by using button click but it is throwing error by sayng that can not read property push of undefined.
I have did some research on history which is used forfunctional component.
How i can make use of history in class component or what is other way (react way) to navigate other page.
here is my code.
import React, { Component } from 'react';
class CentralBar extends Component {
constructor(props){
super(props)
this.state = {
}
this.someText = this.someText.bind(this);
}
someText(){
this.props.history.push(`/login/`);
}
render() {
return (
<div className="crentralPanel">
<button type="button" class="btn btn-light " onClick={this.someText}>Click on Text</button>
</div>
);
}
}
export default CentralBar;
history can be used in a class component. You just get access to it in a different way than in a function component.
In a function component, you would typically use the useHistory hook. But since hooks are exclusive to function components, you have to use a different method in a class.
Probably the simplest way is to use the withRouter higher order component. The only change you need to make is adding the import at the top, and wrapping the export.
import { withRouter } from 'react-router-dom';
export default withRouter(CentralBar);
This wrapper injects 3 props into the component: history, match, and location.
Note that the same rules apply to the HOC as do the useHistory hook since it's just another way of reading from the react-router context: the component must be a child of a Router provider component.
Please wrap your class component with withRouter.
import { withRouter} from 'react-router-dom';
export default withRouter(CentralBar);
If you didn't install react-router-dom module, please run this command to install this node module.
Please let me know if it works or not.
You can't use hooks in class components. You can find many solution in the Internet how to get it around, but I don't think it is worth it. If you really need use hooks in this component, make it with Functional Component. Class components should be used in react in small amount of cases. You can read about it here: React functional components vs classical components

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

How do I properly extend React Bootstrap components?

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.

Pass parameter from non-react jquery html form page to react app

I am new to reactJS.
I have a simple react app that runs fine independently.
Now I am calling it from exising HTML page and want to pass a parameter to react app and use that parameter inside react app.
I googled and found that window environment variable can be set and can be picked in the react app.
How do I do that? Any thoughts or samples, please?
Thanks!
Here is my scenario,
<html>
...
<body>
...
call react app
<input type="hidden" id="empno" value="1000" />
....
</body>
</html>
----
React app
index.js
--------------
import React from 'react';
import ReactDOM from 'react-dom';
import './index.css';
import App from './App';
let eno = document.getElementById("empno");
ReactDOM.render(<Products empnumber={eno}/>, document.getElementById('root'));
app.js
------
...
render() {
console.log(this.props.empnumber);
console log displays null. Thanks!
You may find this article useful:
The way our React component receives data from the outside world is via its props. Whenever we need to update the component with new data — and this is the part that feels magic in its simplicity — we just render it again using the new props, and React knows how to do it efficiently.
In a nutshell, find the part that calls ReactDOM.render(<App/>, container) or similar, and pass your value as a property to App, such as <App someprop={somevalue}/>
You can pass values to your React component when you're mounting it to the dom like so:
const someJson = {
value1: '123',
value2: '456',
};
render(<App someProp={someJson} />, document.getElementById('root'));
const querystring=window.location.search;
const urlParams=new URLSearchParams(querystring);
const empnumber=urlParams.get('empnumber');
You can then pass the variables as props to your component
If you are accessing your react app via a link as you described you could use query strings?
call react app
then in your component you can access the query string like this
render() {
console.log(this.props.location.query.empnumber);
}
there is also a nice npm library to access query string from React component

Using scripts in React without bundling

I'm trying to use Rellax, a JS parallax library, in my React app.
I've downloaded Rellax through npm and imported Rellax into a React component. Rellax's docs then require assigning a rellax variable to the new Rellax constructor, which takes as an argument the query selector.
Like so:
var rellax = new Rellax('.rellax');
However, when I call the component, I receive this error:
Error: The elements you're trying to select don't exist.
However, I do use this class in the component itself.
Below is the full component:
import React from 'react';
import Rellax from 'rellax';
import 'sample.css';
var rellax = new Rellax('.rellax');
const SampleComponent = () => {
return (<div>
<div className="rellax square"></div>
<div className="rellax rectangle"></div>
</div>);
}
export default SampleComponent;
Does anyone have any idea why this isn't working?
Below is a link to Rellax docs: https://github.com/dixonandmoe/rellax
Thanks!
That new Rellax('.relax') constructor is called as soon as that file is loaded (ie, imported by another component). But when the file is loaded, obviously the component hasn't been rendered yet, so the .rellax elements aren't in the DOM.
Instead, you need to call the Rellax constructor when you know the component has rendered. That's what React's componentDidMount is used for. It gets called when the component has been rendered in the DOM (so libraries that use DOM elements can find the element they need).
import React from 'react';
import Rellax from 'rellax';
import 'sample.css';
class SampleComponent extends React.Component {
componentDidMount() {
// We can keep a reference to Rellax in case we need it later
this.rellax = new Rellax('.rellax')
}
render() {
return (
<div>
<div className="rellax square"></div>
<div className="rellax rectangle"></div>
</div>
)
}
}
export default SampleComponent

Categories

Resources