Why does importing a class make this code fail? - javascript

I'm a real beginner in javascript / React...but I'm trying to set-up a tag based on a string value. Why does widget1 fail to get instantiated? (I get an uncaught ReferenceError: FooA is not defined error) What difference does importing the react component make, versus defining it in the same file?
import React, {Component} from "react";
import ReactDOM from "react-dom";
// Assume FooA and FooB have identical definitions
import FooA from './fooa.jsx'
class FooB extends Component {
render() {
return(<p>Hello A</p>);
}
};
class Splash extends Component {
render() {
var widget1 = eval('new ' + "FooA")
var widget2 = eval('new ' + "FooB")
return (
<div>
{(widget1.render())}
{(widget2.render())}
</div>
)
};
}
ReactDOM.render(<Splash/>, container);
I am passing this through webpack to get a single .js file.
Is there a better way to achieve this?

You dont have to instantiate Components, React does that for you.
Your Splash component should look like this:
class Splash extends Component {
render() {
return (
<div>
<FooA />
<FooB />
</div>
)
};
}
Now lets supouse you want to have some logic to determine which component must be rendered:
class Splash extends Component {
let comp = (<FooA />);
if(some condition)
comp = (<FooB />);
render() {
return (
<div>
{comp}
</div>
)
};
}
Now let supouse you want just to parametrize the text:
class FooA extends Component {
render() {
return(<p>this.props.textToShow</p>);
}
};
class Splash extends Component {
let text = 'whatever text you want to show';
render() {
return (
<div>
<FooA textToShow={text}/>
</div>
)
};
}
You can pass as a prop other components as well:
class FooA extends Component {
render() {
return(
<p>Some text</p>
{this.props.child}
);
}
};
class FooAChild extends Component {
render() {
return(
<p>I am a child</p>
);
}
};
class Splash extends Component {
let child = (<FooAChild />);
render() {
return (
<div>
<FooA child={child}/>
</div>
)
};
}

You're coming at this problem from the wrong angle. If you want to make a component able to render other components in a generic, reusable way, there's three approaches you can take:
Pass the component class as a prop
class Splash extends Component {
render() {
let heading = this.props.heading;
// These have to start with a capital letter, otherwise
// JSX assumes 'widget1' is a normal HTML element.
let Widget1 = this.props.widget1;
let Widget2 = this.props.widget2;
return (
<div>
<h1>{heading}</h1>
<Widget1 />
<Widget2 />
</div>
)
};
}
// This can then be used like so:
<Splash heading="My Generic Splash" widget1={FooA} widget2={FooB} />
Pass the component instance as a prop:
class Splash extends Component {
render() {
let heading = this.props.heading;
let widget1 = this.props.widget1;
let widget2 = this.props.widget2;
return (
<div>
<h1>{heading}</h1>
{widget1}
{widget2}
</div>
)
};
}
// This can then be used like so:
let fooA = <FooA />;
<Splash heading="My Generic Splash" widget1={fooA} widget2={<FooB />} />
Pass the components as children:
class Splash extends Component {
render() {
let heading = this.props.heading;
return (
<div>
<h1>{heading}</h1>
{this.props.children}
</div>
)
};
}
// This can then be used like so:
<Splash heading="My Generic Splash">
<FooA />
<FooB />
</Splash>

Related

How to call an event handler function from App.js in two other components

I created a reset function in App.js and want to call it by an onclick in two other components. the problem is that it works in one component but doesn't in the other.
Here are the codes snippets
App.js
import React from 'react';
import Result from './components/Result';
import GeneralResult from './components/GeneralResult';
class App extends Component {
constructor(props) {
super(props);
this.state = {
result: '',
counter: 0,
}
}
// Reset function
handleReset=()=>{
this.setState({
result: '',
counter: 0,
)}
renderResult() {
return (
<div>
<Result reset={()=>this.handleReset()} />
<GeneralResult back={()=>this.handleReset()} />
</div>
);
}
Result.js
first component making use of reset()
function Result(props) {
return (
<div>
<span>
<button onClick={props.reset}>Replay</button>
</span>
</div>
);
}
export default Result;
GeneralResult.js
second component making use of the reset
import React, { Component } from 'react';
export default class GeneralResult extends Component {
render() {
return (
<React.Fragment>
<h2>Congratulations you won!</h2>
<span>
<button onClick={props.back}> Back to Question</button>
</span>
</React.Fragment>
);
}
}
You can pass the handler as props, and render the component from the parent class.
class Child extends Component {
render(){
return(
<button onClick = {this.props.onClick}></button>
)
}
}
export default Child;
import Child from 'path/to/child';
class Parent extends Component {
onClick = (e) => {
//do something
}
render () {
return(
<Child onClick = {onCLick}/>
)
}
}
Problem is that GeneralResult is class based component. so when you need to access props passed to it. you have to use this.props.
export default class GeneralResult extends Component {
render() {
return (
<React.Fragment>
<h2>Congratulations you won!</h2>
<span>
// you need to change "props.back"
// to "this.props.back"
<button onClick={this.props.back}> Back to Question</button>
</span>
</React.Fragment>
);
}
}

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

Get name of wrapped component from its higher order component

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 })

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} />

How to convert const reactjs component to class based

I am trying to figure out how to convert this code
const Child = ({ match }) => (
<div>
<h3>ID: {match.params.id}</h3>
</div>
)
Into a class based component like this
class Home extends React.Component {
render() {
....
}
}
Normal const components I know how to convert, but I am not able to understand how to include match parameter in class based component.
In your functional component definition
const Child = ({ match }) => (
<div>
<h3>ID: {match.params.id}</h3>
</div>
)
The argument is the props passed down to the Child component, however when you use {match} you are detructuring only the prop match from all the props passed down.
See this answer: What is the children prop in React component and what PropTypes do
So when converting your functional component to the class component you can destructure the prop match in the render function like
class Child extends React.Component {
render() {
var {match} = this.props;
return (
<div>
<h3>ID: {match.params.id}</h3>
</div>
)
}
}
function:
const Child = ({ match }) => (
<div>
<h3>ID: {match.params.id}</h3>
</div>
)
Class:
import React, { Component } from 'react';
class Child extends Component {
render(){
const {match} = this.props;
return (
<div>
<h3>ID: {match.params.id}</h3>
</div>
);
}
}
export default Child;

Categories