Spreading props in React Higher Order Components - javascript

I am trying to go very in-depth to understand the purpose of spreading props in React HOC
So taking the below example;
const EnhanceComponent = BaseComponent => {
return class EnhancedComponent extends Component {
state = {
name: 'You have been enhanced'
}
render() {
return (
<BaseComponent {...this.props} {...this.state} />
)
}
}
};
export default EnhanceComponent;
Now let's say the usage of BaseComponent is as below;
<BaseComponent className='wrapper-container' onClick={this.handleClick} />
I assume if had not spread the props in the HOC, we would have been unable to access "this.props.className" OR "this.props.onClick" in BaseComponent. Would that be correct understanding ?
class BaseComponent extends React.Component {
render() {
const { className, onClick} = this.props;
...
}
}
Now to use the HOC itself, we would say;
const EnhancedMyComponent = EnhanceComponent(MyComponent);
And render it as
<EnhancedMyComponent className='wrapper-container' onClick={this.handleClick} />
Now, below are my 2 specific questions;
What do we finally render i.e. BaseComponent or EnhancedMyComponent OR using HOC allows us to use either flavor e.g. in some case, if we do not want the enhanced functionality, we just use the base component ?
OR
<EnhancedMyComponent className='wrapper-container' onClick={this.handleClick} />
Would the props access issue i.e. if we do not spread the props be applicable in both the above cases of consumption i.e. <BaseComponent /> AND <EnhancedMyComponent /> ?

1) What do we finally render i.e. BaseComponent or EnhancedMyComponent OR using HOC allows us to use either flavor e.g. in some case, if we do not want the enhanced functionality, we just use the base component ?
/ Using HOC allows us to use either flavor. It totally depends where we are wrapping the Component in HOC i.e while exporting or while using it at someplace.
Now, In the below case one has the option to use it with or without HOC
// BaseComponent.js
class BaseComponent extends React.Component {
render() {
const { className, onClick} = this.props;
...
}
}
export default BaseComponent;
// SomeComponent.js
import BaseComponent from './BaseComponent';
const MyComponent = EnhanceComponent(BaseComponent);
class SomeComponent extends React.Component {
render() {
return (
...
<MyComponent className={...} onClick={...} someExtraPropForHOC={...}/>
<BaseComponent className={...} onClick={...} />
...
)
}
To not allow anyone to directly use the Component, wrap it in HOC and export
// BaseComponent.js
class BaseComponent extends React.Component {
render() {
const { className, onClick} = this.props;
...
}
}
export default EnhanceComponent(BaseComponent);
// SomeComponent.js
import BaseComponent from './BaseComponent';
class SomeComponent extends React.Component {
render() {
return (
...
<BaseComponent className={...} onClick={...}/>
...
)
}
2) Would the props access issue i.e. if we do not spread the props be applicable in both the above cases of consumption i.e. AND ?
/ Spread the props is needed as HOC does not know what props would be needed for the dynamically wrapped component. So pass all the props which are coming is the only possible way.
class BaseComponent extends React.Component {
render() {
const { className, onClick} = this.props;
...
}
}
class CustomTextField extends React.Component {
render() {
const { className, onKeyPress, value} = this.props;
...
}
}
const EnhancedBaseComponent = EnhanceComponent(BaseComponent);
const EnhancedTextComponent = EnhanceComponent(CustomTextField);
Now in this case EnhancedBaseComponent and EnhancedTextComponent both need different props, but since they are wrapped in EnhanceComponent. It won't know which props to pass. So spread it and send all the props coming to it.

Related

How to properly create and render functional components?

I am attempting to create and render a functional component using the instructions here as a base. From what I've sen in there I should be able to do something along the lines of:
class MyComponent extends React.Component {
render() {
return (
<div>
<OtherComponent props="test" />
</div>
)}
function OtherComponent(props) {
return (
<div>
test
</div>
);
}
}
But this throws the error:
Unexpected token: function OtherComponent(props) {
^
I found a few posts that suggested removing the function so I tried that but then it throws the error:
OtherComponent is not defined
I'm able to get it working by creating a separate class component like so:
class OtherComponent extends React.Component {
render() {
But that's not what I want to do. What is the proper way to create/render a functional component in React.js?
For example this one works. See the docs ;)
React - Composing Components
function OtherComponent(props) {
return <div>test</div>;
}
class App extends React.Component {
render() {
return (
<div>
<OtherComponent props="test" />
</div>
);
}
}
Try this
class MyComponent extends React.Component {
OtherComponent = (props) => {
return (
<div>
test
</div>
);
}
render() {
return (
<div>
{this.OtherComponent("test")}
</div>
)}
}
You can't define a component inside of another component. A functional component means that the component is created from a function and is not a class. It can't have it's own state, because the state is initialized in class constructor. Check out this article for more info https://hackernoon.com/react-stateless-functional-components-nine-wins-you-might-have-overlooked-997b0d933dbc
const otherComponent = (props) =>
<div>
test
</div>;
Here is another way. Its not correct to declare a component in a render function. If it is used solely in a parent component why not make that explicit and use static
class MyComponent extends React.Component {
static myOtherComponent = (props) => <div>{'test'}</div>
render(){
return(
<div>
<MyComponent.myOtherComponent {props} />
</div>
)
}
The myOtherComponent behaviour is controlled purely through the props it gets , it won't have its own state.
Or you could just make it a separate component e.g
export default myOtherComponent = (props) => ()
and import it into MyComponent. Please note , now with hooks ( see React Docs ), you can use hooks to mimic state etc in functional components and the latter approach might be your cleanest and most flexible approach.
This way you can define a function component
function OtherComponent(props) {
return <div>{props}</div>;
}
And now you can use functional component in your App (class component) like below
class App extends React.Component {
render() {
return (
<div>
<OtherComponent props="test" />
</div>
);
}
}

React: parent component props in child without passing explicitly

Is it possible to have the props of the parent component to be available in child component without passing them down?
I am trying to implement a provider pattern, so that to access all the provider props in its child components.
EX:
Suppose the below provider comp FetchProvider will fetch the data and theme props on its own, and when any child component is enclosed by it, I want to access both props "data" and "theme" in the child component as well. How can we achieve it?
class FetchProvider
{
proptypes= {
data: PropTypes.shape({}),
theme: PropTypes.shape({})
}
render()
{
// do some
}
mapStateToProps()
{
return {data, theme};
}
}
class ChildComponent
{
proptypes= {
name: PropTypes.shape({})
}
render()
{
const{data, them} = this.props; // is this possible here?
// do some
}
}
and if I try to above components as below.
<FetchProvider>
<ChildComponent name="some value"/> //how can we access parent component props here? without passing them down
<FetchProvider/>
This is exactly what react context is all about.
A Consumer can access data the a Provider exposes no matter how deeply nested it is.
// Context lets us pass a value deep into the component tree
// without explicitly threading it through every component.
// Create a context for the current theme (with "light" as the default).
const ThemeContext = React.createContext('light');
class App extends React.Component {
render() {
// Use a Provider to pass the current theme to the tree below.
// Any component can read it, no matter how deep it is.
// In this example, we're passing "dark" as the current value.
return (
<ThemeContext.Provider value="dark">
<Toolbar />
</ThemeContext.Provider>
);
}
}
// A component in the middle doesn't have to
// pass the theme down explicitly anymore.
function Toolbar(props) {
return (
<div>
<ThemedButton />
</div>
);
}
function ThemedButton(props) {
// Use a Consumer to read the current theme context.
// React will find the closest theme Provider above and use its value.
// In this example, the current theme is "dark".
return (
<ThemeContext.Consumer>
{theme => <Button {...props} theme={theme} />}
</ThemeContext.Consumer>
);
}
Here is a small running example:
Note This is the react v16 context API.
Your use case can be solved with the usage of React context. With the help of Context, any child that is wrapped by a provided can be a consumer for the data that is provided by the Provider
In your case, you can use it like
context.js
export const FetchContext = React.createContext();
Provider.js
import { FetchContext } from 'path/to/context.js';
class FetchProvider extends React.Component
{
proptypes= {
data: PropTypes.shape({}),
theme: PropTypes.shape({})
}
render()
{
const { data, theme, children } = this.props;
return (
<FetchContext.Provider value={{ data, theme}}>
{children}
</FetchContext.Provider>
)
}
mapStateToProps()
{
return {data, theme};
}
}
ChildComponent.js
class ChildComponent extends React.Component
{
proptypes= {
name: PropTypes.shape({})
}
render()
{
const{data, them} = this.props; // use it from props here
// do some
}
}
export default (props) => (
<FetchContext.Consumer>
{({ data, theme }) => <ChildComponent {...props} data={data} theme={theme} />}
</FetchContext.Consumer>
)
However given the fact that you are already using Redux, which is build on the concept of Context, you might as well use redux and access the values within the child component since they are the same values that are supplied from the Redux store to the child by parent.
class ChildComponent extends React.Component
{
proptypes= {
name: PropTypes.shape({})
}
render()
{
const{data, them} = this.props; // use it from props here
// do some
}
}
const mapStateToProps = (state) => {
return {
data: state.data,
theme: state.theme
}
}
You can use React.Children to iterate over the children and pass whatever props you want to send to the new cloned elements using React.cloneElement.
EX:
class Parent extends React.Component {
constructor(props) {
super(props);
}
render() {
const { children } = this.props;
const newChildren = React.Children.map(children, child =>
React.cloneElement(child, { myProp: 'test' }));
return(
<View>
{newChildren}
</View>
)
}
}
Are you looking for:
class MyParent extends Component {
render() {
return <MyChild {...this.props}>
// child components
</MyChild>
}
}
This would pass all of the props passed into MyParent to the MyChild being rendered.

React anti pattern?

Is the following an anti pattern in React? I like the pattern because it gives me context in static functions when a component has been instantiated. Then later I can import the class and call a static method to modify state. Or can this be done in a better way?
// componentA.js
function bleedContext() {
ComponentA.staticMethod = ComponentA.staticMethod.bind(this)
}
export default class ComponentA {
static staticMethod() {
this.setState({foo: 'bar'})
}
constructor() {
this.state = {}
bleedContext.call(this)
}
render() {
return (
...
)
}
}
// componentB.js
import ComponentA from 'path/to/componentA'
export default class ComponentB {
handleClick() {
ComponentA.staticMethod()
}
render() {
return (
<button onClick={this.handleClick} />
)
}
}
This is clearly an antipattern and possibly a mistake, depending on conditions. Static class method shouldn't operate with class instance. staticMethod is bound to specific component instance and uses setState, this could be only justified a class is a singleton (though a singleton is often an antipattern, too). This will result in bugs and memory leaks if more than one class instance is expected, and every React component is expected to have more than one instance, at least for testing.
A proper way for two independent components to interact with each other in React is to have a common parent component that provides this interaction, e.g.:
class ModalContainer extends Component {
modalRef = React.createRef();
render() {
return <>
<Modal ref={this.modalRef} />
<SomeComponentThatUsesModal modalRef={this.modalRef} />
</>;
}
}
The problem with example above is that this will require to pass modalRef prop deeply if <SomeComponentThatUsesModal> is nested.
This problem is solved with React context or other third-party global state solutions like Redux.
This can be done with React 16.3 context API, considering that Modal class instance has open method:
const ModalContext = React.createContext();
function getModal(modalRef) {
return {
open: data => modalRef.current.open(data);
close: () => modalRef.current.close();
}
}
class ModalContainer extends Component {
modalRef = React.createRef();
render() {
return <>
<Modal ref={this.modalRef} />
<ModalContext.Provider value={getModal(this.modalRef)}>
{this.props.children}
</ModalContext.Provider>
</>;
}
}
Then for any deeply nested component modal object with open and close methods will be available via context:
const SomeComponentThatUsesModal = props => <div>
<ModalContext.Consumer>
{modal => <button onClick={() => modal.open('foo')} />}
</ModalContext.Consumer>
</div>;
<ModalContainer>
...deeply nested component
<SomeComponentThatUsesModal />
...
</ModalContainer>
Here's a demo.

React HOC - Access wrapped component function

PRESENTATIONAL COMPONENT
class ClientsPage extends React.Component {
_myFunction() {
//do what needs to be done
}
render() {
return <div></div>
}
}
export default doMagic(ClientsPage)
HOC COMPONENT
export const doMagic= (WrappedComponent) => {
return class MyMagic extends React.Component {
render() {
const props = Object.assign({}, this.props , {
xxx: ???,
});
return <WrappedComponent { ...props } />
}
};
}
Hi guys, i have react component and want to transform it in some way in my HOC component.
But heres the problem. I want to create another prop lets call it xxx in HOC. This prop will be of type object and one of properties of this object should be function from wrapped component so womething like
xxx : {callback : reference to function from wrapped component}
Is this even possible ?
thx in advance
You can do callback to wrapped component's function with WrappedComponent.prototype._myFunction()
const doMagic= (WrappedComponent) => {
return class MyMagic extends React.Component {
render() {
const props = Object.assign({}, this.props , {
xxx: WrappedComponent.prototype._myFunction()
});
return <WrappedComponent { ...props } />
}
};
}
class ClientsPage extends React.Component {
_myFunction() {
return "Wrapped Component Function Callback Done..!";
}
render() {
return <div>Hello {this.props.xxx}</div>
}
}
export default doMagic(ClientsPage)
You can see the working jsfiddle here https://jsfiddle.net/12ojjddw/
If you want to access WrappedCompoent props, then you need to use Inheritance Inversion, it is a bit more complex, but allows you full control, here is a good explanation:
https://medium.com/#franleplant/react-higher-order-components-in-depth-cf9032ee6c3e

React js access to the state of another class

How can I access the state of another class.
This construction does not work
class classname2 extends React.Component {
...
this.state = { statename1: "lala" };
...
};
class classname1 extends React.Component {
render() {
return (
{classname2.state.statename1 }
);
}
};
As mentioned in the comments, pass state as props to their children.
class classname2 extends React.Component {
this.state = { statename1: "lala" };
render() {
return <classname1 statename1={this.state.statename1} />
}
};
class classname1 extends React.Component {
render() {
return (
<div>{this.props.statename1}</div>
);
}
};
An often used pattern is passing arbitrary props down the component tree:
const {needThisOne, andThisOne, ...props} = this.props;
// do stuff with needThisOne andThisOne
// and pass the remaining props down:
return <Component {...props} />;
An update for hooks, because why not.
const ParentComponent = ({...props}) => {
const [stateName1, setStateName1] = useState('defaultValue');
return <ChildComponent stateName1={stateName1} {...props} />;
}
const ChildComponent = ({stateName1, ...props}) => (
<span>{stateName1}</span>
);
Shared state between components by direct access is an anti-pattern. Each component should have its own state. If you need globally a available state, please consider using Redux.
It may sound a bit cumbersome at first but it's awesome and it allows your app to be properly tested.
Edit:
Passing state as props is also valid, but it only works when components are in parent-child order. Redux allows components to be updated no matter what their relationship is

Categories

Resources