Get name of wrapped component from its higher order component - javascript

say my HOC is:
import React, { Component } from "react";
let validateURL = WrappedComponent =>
class extends Component{
render() {
if( wrappedcomponentnameis === 'xyz')
return ...
elseif(wrappedcomponentnameis === 'abc')
return ...
and so on....
}
};
export default validateURL;
how do I get the name of wrapped component inside this HOC?

You can access it via WrappedComponent.name:
const HOC = WrappedComponent => class Wrapper extends React.Component{
render() {
if (WrappedComponent.name === 'Hello') {
return <WrappedComponent name='World' />
}
return <WrappedComponent/>
}
}
class Hello extends React.Component {
render() {
return <div>Hello {this.props.name}</div>
}
}
const App = HOC(Hello)
ReactDOM.render(
<App />,
document.getElementById('container')
);
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react-dom.min.js"></script>
<div id="container">
<!-- This element's contents will be replaced with your component. -->
</div>
However, I will prefer to pass optional props to the HOC, in order to control its behavior, because it's much safer, rather than relying on WrappedComponent.name.
For example: there are many libraries (as redux, react-router, and etc) which provide some functionality to your components through HOC mechanism. When this libraries wraps your component, then WrappedComponent.name will point to the library HOC name and will break your logic silently.
Here's how you can pass custom props:
const HOC = (WrappedComponent, props) => class Wrapper extends React.Component{
render() {
const { shouldPassName } = props
if (shouldPassName) {
return <WrappedComponent name='World' />
}
return <WrappedComponent/>
}
}
const App = HOC(Hello, { shouldPassName: true })

Related

Call Method that inside Another Component - React Js

i need to know how to fetch state of component from other component by calling the seconed component method inside of first component ?
like :
class General extends Component {
state = {
input:"
}
fetchState() {
return this.state;
}
handleChange () {
this.setState({[e.target.name]: e.traget.value});
}
render() {
return <input type="text" name="input" onChange={this.handleChange.bind(this}>
}
}
class Car extends Component {
render() {
console.log( General.fetchState() );
return null;
}
}
i know i can use static method but i don't have access to this keyword.
The recommended way of doing that kind of things is by composing components and passing the parent's states as props
class General extends Component {
state = { ... }
render () {
return (
<Car {...this.state} />
)
}
}
class Car extends Component {
render () {
console.log(this.props)
return (...)
}
}
Now if you want to share a global state between components could be a good idea to use context api with hooks.
import React, { createContext, useContext } from "react";
import ReactDom from "react-dom";
const initialState = { sharedValue: "Simple is better" };
const StateContext = createContext({});
const General = () => {
const globalState = useContext(StateContext);
return <h1>General: {globalState.sharedValue}</h1>;
};
const Car = () => {
const globalState = useContext(StateContext);
return <h1>Car: {globalState.sharedValue}</h1>;
};
const App = () => {
return (
<StateContext.Provider value={initialState}>
<General />
<Car />
</StateContext.Provider>
);
};
ReactDom.render(
<App />,
document.getElementById("root")
);
Here is the example link.
And here I have a repo with a more elaborated example managing global state with just hooks.
There are many approaches, I suggest using a general state accessible from both components.
Check ReactN for simplicity or Redux for a more robust solution. Note Redux has a big learning curve and quite some boilerplate that, depending on the size of your App, it could not be necessary.
Using globals is not advisable on many situations, but to answer your question, you could also do this:
General component:
class General extends Component {
constructor(){
global.fetchGeneralState = this.fetchState;
}
fetchState = () => {
return this.state;
}
}
Then from the Car component, you can just call: global.fetchGeneralState(); and you will get the state from the General component.
In your current code, the only way to do it is to use new General.
console.log(new General().fetchState());
If you expect to use Car component as a parent of General component, then you can simply use ref. Here is the modified code of yours that I have tested :
import React from "react";
class General extends React.Component {
constructor () {
super();
this.state = {input: ""}
this.handleChange = this.handleChange.bind(this);
}
fetchState() {
return this.state;
}
handleChange(e) {
this.setState({[e.target.name]: e.target.value});
}
render() {
return <input type="text" name="input" onChange={this.handleChange} />
}
}
export default class Car extends React.Component {
constructor () {
super();
this.refToGeneral = React.createRef()
this.handleClick = this.handleClick.bind(this)
}
handleClick() {
console.log(this.refToGeneral.current.fetchState())
}
render() {
return (
<>
<General ref={this.refToGeneral} />
<button type="button" onClick={this.handleClick}>Show State</button>
</>
)
}
}

Update React component based on global variable updated by another component

I have a global variable called global.language. In my CustomHeader component, I have a Button that toggles the language global variable. What I want is to update all my screen components to reflect the language change.
I don't know if the best way to go is to get a reference to the Screens or to use an event library or if there are React friendly ways of doing this.
My CustomHeader.js looks like this:
export default class CustomHeader extends React.Component {
constructor(props) {
super(props);
this.toggleLanguage = this.toggleLanguage.bind(this);
}
render() {
return (
<Button onPress={ this.toggleLanguage } title="Language" accessibilityLabel="Toggle language" />
);
}
toggleLanguage() {
if (global.language == "PT") global.language = "EN";
else if (global.language == "EN") global.language = "PT";
}
}
My Screen.js renders numerous components called Event. This is what my Event.js looks like:
export default class Event extends React.Component {
constructor(props) {
super(props);
}
render() {
return (
<Card>
<Text>{Event.getTitle(this.props.data)}</Text>
</Card>
);
}
static getTitle(data) {
if (global.language === "PT") return data.title;
else if (global.language === "EN") return data.title_english;
}
}
Live sandbox
In details.
React.createContext we can just export to reuse. But this would be just "generic" context. Better encapsulate data and methods we need into custom container element and HOC:
import React from "react";
const context = React.createContext();
export class I18N extends React.Component {
state = {
language: "en"
};
setLanguage = language => {
this.setState({ language });
};
render() {
return (
<context.Provider
value={{ language: this.state.language, setLanguage: this.setLanguage }}
>
{this.props.children}
</context.Provider>
);
}
}
export function withI18n(Component) {
return props => (
<context.Consumer>
{i18nContext => <Component {...props} i18n={i18nContext} />}
</context.Consumer>
);
}
<I18N> is provider that will typically go just once on the topmost level.
And with HOC withI18n we are going to wrap every element that need access to our language value or ability to update this value.
import React from "react";
import ReactDOM from "react-dom";
import { I18N, withI18n } from "./i18n";
const Header = withI18n(function({i18n}) {
const setLang = ({ target: { value } }) => i18n.setLanguage(value);
return (
<div>
<input type="radio" value="en" checked={i18n.language === "en"} onChange={setLang} /> English
<input type="radio" value="fr" checked={i18n.language === "fr"} onChange={setLang} /> French
<input type="radio" value="es" checked={i18n.language === "es"} onChange={setLang} /> Spanish
</div>
);
});
const Body = withI18n(function(props) {
return <div>Current language is {props.i18n.language}</div>;
});
const rootElement = document.getElementById("root");
ReactDOM.render(<I18N>
<Header />
<Body />
</I18N>, rootElement);
And finally good article with some additional details: https://itnext.io/combining-hocs-with-the-new-reacts-context-api-9d3617dccf0b

How to pass function in another file in React

I have a function that is used to change the state of a react component but I'm trying to pass the function in another file. I get the error that the function I'm trying to pass (changeView) is not defined.
This is the App.js
export default class App extends Component {
constructor() {
super();
this.state = {
language: "english",
render: ''
}
}
changeView(view, e){
console.log(view);
this.setState({render: view});
}
_renderSubComp(){
switch(this.state.render){
case 'overview': return <Overview />
case 'reviews': return <Reviews />
}
}
render() {
const {render} = this.state
return <Fragment>
<Header language={this.state.language} />
<Hero />
<Navigation render={render}/>
{this._renderSubComp()}
</Fragment>;
}
}
I'm trying to pass the changeView method to the Navigation.JS component, so I can change the active link as well as render the components listed in the _renderSubComp method above.
import React from "react";
import "./navigation.css";
import { changeView } from "../app";
export default function Navigation() {
return <div className="navigation">
<a onClick={this.changeView.bind(this,
'overview')}>Overview</a>
<a>Reviews</a>
</div>;
}
How should I pass the function to another file so it's able to change the state of my component and render the component I need.
You can't import a method like that. You will pass your function like any other prop to your component and you use there.
I've changed a few things. Firstly, I define changeView function as an arrow one, so we don't need to bind it. Secondly, I pass this function to the component as a prop. Thirdly, I used this function there like:
onClick={() => props.changeView('overview')}
As you can see it is props.changeView not state.changeView
Just go through the official documentation a little bit more. You are confused about states, props and how to pass them to your components.
class App extends React.Component {
constructor() {
super();
this.state = {
language: "english",
render: ''
}
}
changeView = (view, e) => {
console.log(view);
this.setState({ render: view });
}
render() {
const { render } = this.state
return <div>
<Navigation render={render} changeView={this.changeView} />
</div>;
}
}
const Navigation = (props) => {
return <div className="navigation">
<a onClick={() => props.changeView('overview')}>Overview</a>
<a>Reviews</a>
</div>;
}
ReactDOM.render(<App />, document.getElementById("root"));
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react-dom.min.js"></script>
<div id="root"></div>

Defining and exporting HOC in React

I've been research Higher Order Components in react. My requirement is that I have a set components which I need to extend to give them more functionality without rewriting the entire component. In this case, I found out the concept HOC in react where one could extend the component using a pure function. My question is, can I export the extended component as a normal component. For an example
Component which needs to be extended
class foo extends React.Component {
render(){
//something
}
}
export default foo;
HOC component
function bar(foo) {
render() {
return <foo {...this.props} {...this.state} />;
}
}
export default bar;
Am I able to use the component that way? or am I doing it wrong?
A HOC would take a component, add some more functionality and return a new component and not just return the component instance,
What you would do is
function bar(Foo) {
return class NewComponent extend React.Component {
//some added functionalities here
render() {
return <Foo {...this.props} {...otherAttributes} />
}
}
}
export default bar;
Now when you want to add some functionality to a component you would create a instance of the component like
const NewFoo = bar(Foo);
which you could now use like
return (
<NewFoo {...somePropsHere} />
)
Additionally you could allow the HOC to take a default component and export that as a default component and use it elsewhere like
function bar(Foo = MyComponent) {
and then create an export like
const wrapMyComponent = Foo();
export { wrapMyComponent as MyComponent };
A typical use-case of an HOC could be a HandleClickOutside functionality whereby you would pass a component that needs to take an action based on handleClickOutside functionality
Another way could be like this:
Make a Foo Component
class Foo extends React.Component {
render() {
return ( < h1 > hello I am in Foo < /h1>)
}
}
Make a HOC component.
class Main extends React.Component {
constructor(props) {
super(props);
}
render() {
const {
component, props
} = this.props;
//extract the dynamic component passed via props.
var Component = component;
return ( < div >
< h1 > I am in main < /h1>
< Component {...props} > < /Component>
</div > );
}
}
ReactDOM.render( < Main component = {
Foo
} > < /Main>,
document.getElementById('example')
);
Working code here
Yes you can
const bar = (Foo) => {
return class MyComponent extend Component {
render() {
return <Foo {...this.props} />
}
}
}
//Our Foo Component Code Here
export default bar(Foo)
But again it depends on the functionality. Eg: suppose you're using react router and want to check if user is present before rendering the component don't pass the HOC. eg:
<Route path="/baz" component={auth(Foo)} />
Instead use an new component.
Note: NewComponent is connected to redux and user (state) is passed as props
class NewRoute extends Component{
render(){
const {component:Component, ...otherProps} = this.props;
return(
<Route render={props => (
this.props.user? (
<Component {...otherProps} />
):(
<Redirect to="/" />
)
)}
/>
);
}
}
Then on the routes
<NewRoute path='/foo' component={Foo} />

Can class object be called from React Component Prop?

I'm studying ReactNative.Navigator.renderScene props.
'use strict';
import React,{Component} from 'react';
import ReactNative from 'react-native';
const {
TouchableHighlight,
Navigator,
AppRegistry,
Text,
View,
} = ReactNative;
class TestClass extends Component{
render(){
return <Text>test</Text>
}
}
class MyTag extends Component{
render(){
return <Text>test</Text>
}
}
class Main extends Component{
render(){
const routes =[{component:TestClass,index:0},{component:MyTag,index:1}]
return(
<Navigator
initialRoute={routes[0]}
initialRouteStack={routes}
renderScene={(route, navigator) =>
<View><TouchableHighlight onPress={() => {
if (route.index === 0) {
navigator.push(routes[1]);
} else {
navigator.pop();
}
}}><View>{route.component}</View>
</TouchableHighlight></View>
}
/>
)
}
}
AppRegistry.registerComponent('ChoiceComponent', () => Main);
Can component in routes variable be called by using {route.component} in renderScene props in JSX?
TestClass is called correctly if {route.component} is changed into <Test Class />.
You're asking if you can use an object property (route.component) in place of a class name. Absolutely! Remember, these are just identifiers. You use it exactly the same way you used the class name.
So instead of
{route.component}
you want
<route.component />
(But keep reading, we may have to do more.)
Example:
class Example1 extends React.Component {
render() {
return <div style={{color: "blue"}}>{this.props.text}</div>;
}
}
class Example2 extends React.Component {
render() {
return <div style={{color: "green"}}>{this.props.text}</div>;
}
}
const routes = [
{component: Example1},
{component: Example2}
];
ReactDOM.render(
<div>{routes.map(route => <route.component text="Hi there" />)}</div>,
document.getElementById("react")
);
<div id="react"></div>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react-dom.min.js"></script>
The above works, but as far as I can tell from the React documentation, our component identifier name should start with a capital letter:
User-Defined Components Must Be Capitalized
When an element type starts with a lowercase letter, it refers to a built-in component like <div> or <span> and results in a string 'div' or 'span' passed to React.createElement. Types that start with a capital letter like <Foo /> compile to React.createElement(Foo) and correspond to a component defined or imported in your JavaScript file.
In our case, it's route.component, which is currently handled correctly (because of the .; it wouldn't if it were route_component, for instance), but that appears to be undocumented behavior. (Supporting the . is documented behavior, what appears undocumented is allowing you to start with a lower-case letter when it's not a simple identifier.)
So I think to be officially in line with the docs, we'd want to assign that to a capitalized identifier:
const RouteComponent = route.component;
return <RouteComponent text="Hi there" />;
Like so:
class Example1 extends React.Component {
render() {
return <div style={{color: "blue"}}>{this.props.text}</div>;
}
}
class Example2 extends React.Component {
render() {
return <div style={{color: "green"}}>{this.props.text}</div>;
}
}
const routes = [
{component: Example1},
{component: Example2}
];
ReactDOM.render(
<div>{routes.map(route => {
const RouteComponent = route.component;
return <RouteComponent text="Hi there" />;
})}</div>,
document.getElementById("react")
);
<div id="react"></div>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react-dom.min.js"></script>

Categories