While learning higher order component in react, I was trying to use it in my code. While doing so, I am getting "Invariant Violation Error"
//Higher Order Component
const withStyles = (OriginalComponent) => {
class NewComponent extends React.Component{
render(){
return <OriginalComponent />
}
}
return NewComponent;
}
class FancyButton extends React.Component {
render() {
return <button>Fancy button</button>;
}
}
const App = props => {
//I am calling the HOC to get my updated component
let Enhanced = withStyles(<FancyButton />);
return (
<Enhanced />
);
};
ReactDOM.render(<App />, document.getElementById("root"));
Here I am calling the HOC(withStyles) and storing the returned component in a variable "Enhanced". In the App component, Can I directly call Enhanced component like ?
I expect the output should be button element, but I am getting "Invariant Violation" error
You are passing a rendered result of FancyButton, whereas you should've passed the actual component function
See below:
...
const App = props => {
//I am calling the HOC to get my updated component
let Enhanced = withStyles(FancyButton);
return (
<Enhanced />
);
};
...
Related
I have a component which is going through an hoc, but i want to get some props of this component inside the hoc. All works fine but i can not find out how to get the props out of this child component into the hoc.
here is the component which is going through the hoc, and that is this 'getAction' props i want to extract in the hoc
class ProjectPage2 extends Component {
render() {
return (
<Project2 getAction="getAction"/>
);
};
};
export default PageHandler(ProjectPage2)
here is the hoc component (imported as PageHandler in the ProjectPage2)
export default (ChildComponent) => {
class ComposedComponent extends Component {
render() {
// here i want to get the 'getAction' props, which is inside this ChildComponent
// because i need to use it into this hoc logic
return <ChildComponent {...this.props} />;
}
}
const mapStateToProps = (state) => {
return {
comments: state.project2
}
};
const loadData = (store) => {
return store.dispatch(getProject2());
};
return {
loadData,
component: connect(mapStateToProps, { getProject2 })(ComposedComponent)
}
};
if some one have an idea it would be great. Thanks.
I think you're very close already. It looks to me like you want the final result to be something like:
<Project2 getAction="getAction" comments={...} />
But what ends up getting rendered is just:
<Project2 getAction="getAction" />
See, the custom props of your HOC are passed to your child component via the child's props. You aren't using those, so the child just completely ignores the HOC. You can fix this with:
class ProjectPage2 extends Component {
render() {
return (
<Project2 getAction="getAction" {...this.props} />
);
};
}
Thank you for the answer. But it s not what i was looking for. I wanted to get into the HOC some children component's props passing through. But i finally get the idea which solved it. In fact it s so simple......
i wanted to pass the "getAction" string into the HOC. But i didn t find any solution to extract it (from the passing through component) there.
The solution is simply to pass it into the export default
class ProjectPage2 extends Component {
render() {
return (
// i was trying to use the component props
// <Project2 getAction="getAction"/>
// but no need
<Project2 />
);
};
};
// pass it into the fonction fix it
export default PageHandler(ProjectPage2, "getAction")
then get it in the HOC
export default (ChildComponent, varAction) => {
class ComposedComponent extends Component {
console.log(varAction) // return getAction
I am learning HOCs and keep reading the above quote, but I do not understand what it means. If my HOC adds a method to my consuming component, can I use that method in the render method like so? If not how would I do what I am trying to do here:
import React, { Component } from 'react';
import { withMyHOC } from '../with_my_component'
class MyComponent extends Component {
constructor(props) {
super(props);
}
render() {
const { methodFromHOC }= this.props;
const result = methodFromHOC(someArgument);
return (
<div >
{result}
</div>
)
}
}
export default withMyHOC(MyComponent );
When you say, do not use HOC within the render method, it means that you shouldn't create an instance of the component wrapped by HOC within the render method of another component. For example, if you have a App Component which uses MyComponent, it shouldn't be like below
import React, { Component } from 'react';
class MyComponent extends Component {
constructor(props) {
super(props);
}
render() {
const { methodFromHOC }= this.props;
const result = methodFromHOC(someArgument);
return (
<div >
{result}
</div>
)
}
}
export default MyComponent;
import { withMyHOC } from '../with_my_component'
export default class App extends React.Component {
render() {
const Wrap = withMyHOC(MyComponent);
return (
<div>
{/* Other Code */}
<Wrap />
</div>
)
}
}
Why you shouldn't use it like above is because everytime render method is called a new instance of the MyComponent is created wrapped by HOC called Wrap and hence everytime it be be mounted again instead of going by the natural lifecycle or React.
However if your HOC passes a function as props, you can use it within the render as long as it doens't cause a re-render again otherwise it will lead to a infinite loop.
Also its better to memoize functions which are called in render directly to avoid computation again and again
CodeSandbox Demo
A High Order Component is a function which returns a Component, not jsx. When wrapping a component with an hoc, you're not changing the returned value of your component, you're changing the signature itself. Consider the following hoc
const withFoo = Component => props =>{
return <Component {...props} foo='foo' />
}
withFoo is a function which takes a Component (not jsx) as argument and returns a component. You don't need to call an hoc from render because the values it injects are already inside props of the wrapped component.
An hoc tells how a wrapped component will look like, changes it's definition so the only place to use it is in the component definition itself. Calling an hoc inside render creates a new instance of that component on each render. It's the equivalent of
const Component = () =>{
const ChildComponent = () =>{
return <span> Child </span>
}
return <ChildComponent /> //You're declaring again on each render
}
Use your high order components like this
const Component = ({ foo }) => <div>{ foo }</div>
export default withFoo(Component)
Or
const Component = withFoo(({ foo }) => <div>{ foo }</div>)
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
I am a beginner in React. Looking at a few medium articles and React docs(which is complicated) I have tried to implement this very basic Context API.
I have missed some basic point which is the reason why I haven't got the correct result which is to pass data through the components tree and access them in the child component.
Please let me know how to correct given code snippet and what have I missed.
import React from 'react';
import './index.css';
const AppContext = React.createContext();
function GreenBox () {
return <div className='green-box'>
<AppContext.Consumer>
{(context) => context.value}
</AppContext.Consumer>
</div>
}
function BlueBox () {
return <div className='blue-box'><GreenBox/></div>
}
class RedBox extends React.Component {
render() {
return <div className='red-box'>
<AppContext.Consumer>
{(context) => context.value}
</AppContext.Consumer>
<BlueBox/>
</div>
}
}
class Context extends React.Component {
state = {
number: 10
}
render() {
return (
<AppContext.Provider value = {this.state.number}>
<RedBox/>
</AppContext.Provider>
)
}
}
export default Context;
The value you set in the Provider will be the argument received in the render props function in Consumer, so instead of accessing the number you're expecting with context.value, you should just change to context.
In my APP, which is the parent component of my react-router, I have this:
export default class APP extends Component {
static propTypes = {
children: PropTypes.element
};
constructor(props) {
super(props);
this.state = {
clientId: 'A134567897',
..........................
};
}
componentDidCount() {
CompanyInfo.getGenlInfo()
.then((response) => {
const data = response.data;
this.setState(data);
});
}
renderChild = () =>
React.cloneElement(this.props.children, {
// this needs to be passed to all child routes
...this.state
});
render() {
return (
<section>
<Header { ...this.state } />
{/* This is where the dynamic content goes */}
{ React.Children.map(this.props.children, this.renderChild) }
<Footer { ...this.state } />
</section>
);
}
}
In my router, a path of '/' brings up the WelcomePage (main page) component.
The WelcomePage component does appear as expected, and this.props.clientId does have a value, but if I code on the WelcomePage ....
WelcomePage.propTypes = {
clientId: PropTypes.string.isRequired
};
Required prop clientId was not specified in WelcomePage. Check the render method of RoutingContext.
I thought I took care of passing the clientId props to the WelcomePage through the APP's renderChild() method with '...this.state', didn't I?
Does anyone see where the error lies? Again, the clientID value does successfully pass to the WelcomePage, but I receive an error if I make it required on the WelcomePage.
Many Thanks
You can't use .isRequired on propTypes for route components.
This is because React checks propTypes at the time of element creation. React Router will initially create all of the route component elements with just the React Router props; by the time you get to the cloneElement call, this validation has already happened and failed.
We've recently added a note to the upgrade guide detailing this: https://github.com/rackt/react-router/blob/master/UPGRADE_GUIDE.md#routehandler
For more details see: https://github.com/facebook/react/issues/4494#issuecomment-125068868