user5809117
user5809117

Reputation:

How to use this higher order component explained in the Redux Doc?

Could anyone please help me understand how to use this Higher Order Component explained in the redux document?

I know what Higher Order Component is and I have used this patter several times but I can't figure out how to use this Higher Order Component.

http://redux.js.org/docs/recipes/UsingImmutableJS.html#use-a-higher-order-component-to-convert-your-smart-components-immutablejs-props-to-your-dumb-components-javascript-props

Here is what it says...

Use a Higher Order Component to convert your Smart Component’s Immutable.JS props to your Dumb Component’s JavaScript props

Something needs to map the Immutable.JS props in your Smart Component to the pure JavaScript props used in your Dumb Component. That something is a Higher Order Component (HOC) that simply takes the Immutable.JS props from your Smart Component, and converts them using toJS() to plain JavaScript props, which are then passed to your Dumb Component.

Here is an example of such a HOC:

import React from 'react'
import { Iterable } from 'immutable'

export const toJS = WrappedComponent => wrappedComponentProps => {
    const KEY = 0
    const VALUE = 1

    const propsJS = Object.entries(
        wrappedComponentProps
    ).reduce((newProps, wrappedComponentProp) => {
        newProps[wrappedComponentProp[KEY]] = Iterable.isIterable(
            wrappedComponentProp[VALUE]
        )
            ? wrappedComponentProp[VALUE].toJS()
            : wrappedComponentProp[VALUE]
        return newProps
    }, {})

    return <WrappedComponent {...propsJS} />
}

And this is how you would use it in your Smart Component:

import { connect } from 'react-redux'

import { toJS } from './to-js'
import DumbComponent from './dumb.component'

const mapStateToProps = state => {
    return {
        // obj is an Immutable object in Smart Component, but it’s converted to a plain
        // JavaScript object by toJS, and so passed to DumbComponent as a pure JavaScript
        // object. Because it’s still an Immutable.JS object here in mapStateToProps, though,
        // there is no issue with errant re-renderings.
        obj: getImmutableObjectFromStateTree(state)
    }
}
export default connect(mapStateToProps)(toJS(DumbComponent))

By converting Immutable.JS objects to plain JavaScript values within a HOC, we achieve Dumb Component portability, but without the performance hits of using toJS() in the Smart Component.

Here is my sample code for this!!

DumbComponent

import React from 'react';

const DumbComponent = (props) => {

    const {dataList} = props;

    const renderList = (list) => {

        return list.map((value) => {
            return <li>value</li>
        })

    };

    return (
        <ul>
            {renderList(dataList)}
        </ul>
    )
};

export default DumbComponent;

SmartComponent

import React, { Component } from 'react';
import { connect } from 'react-redux';

import DumbComponent from './DumbComponent';

//High Order Component
import toJS from './toJS'; 

class SmartComponent extends Component {

    constructor(props) {
        super(props);
    }

    render() {
        return (
            <DumbComponent dataList={this.props.dataList} />
        );
    }

}


function mapStateToProps(states) {
    return {
        //Let's say this is immutable.
        dataList: state.dataList,
    };
}

//this is how I use Higher Order Component.
//export default connect(mapStateToProps)(SmartComponent);


export default connect(mapStateToProps)(toJS(DumbComponent));

My Question

export default connect(mapStateToProps)(toJS(DumbComponent)); This doesn't even export SmartComponent itself. How can the parent component of SmartComponent whose child is DumbComponent use SmartComponent if it is not even exported??

Please tell me how to use this Higher Order Component in the sample code I prepared for this post.

Update

So, this is the correct way to write?

SmartComponent

import React, { Component } from 'react';
import { connect } from 'react-redux';

import DumbComponent from '../components/DumbComponent';

class SmartComponent extends Component {

  constructor(props) {
    super(props);
  }


  render() {

    return (
      <DumbComponent dataList={this.props.dataList} /> //immutable
    );

  }
}


function mapStateToProps(states) {
  return {
    dataList: state.dataList //immutable
  };
}


export default connect(mapStateToProps)(SmartComponent);

DumbComponent

import React from 'react';
import toJS from './higher_order_components/toJS';


const DumbComponent = (props) => {

  const {dataList} = props;

  const renderList = (list) => {

    return list.map((value) => {
      return <li>{value}</li>
    })

  };

  return (
    <ul>
      {renderList(dataList)}
    </ul>
  )

};


export default toJS(DumbComponent);

Upvotes: 5

Views: 941

Answers (1)

azium
azium

Reputation: 20614

When you do something like:

let Component = connect(mapStateToProps)(OtherComponent)

Component is "smart" because it has access to the store. What you're doing in your code is doubling up on smart components.

class OtherSmartComponent {
  render { return <DumbComponent {...smartProps} /> }
}
let SmartComponent = connect(mapStateToProps)(OtherSmartComponent)

That's why in the example Smart Component there is no intermediate Smart Component, just connect, mapStateToProps and DumbComponent.

Might be worth unraveling it all:

import DumbComponent from './DumbComponent'
import toJS from './toJS'

let DumbComponentWithJSProps = toJS(DumbComponent)
let SmartComponent = connect(mapStateToProps)(DumbComponentWithJSProps)

export default SmartComponent

So really, in your code, SmartComponent isn't really smart. It's just a dumb component that renders another dumb component. The terminology makes this comment seem super harsh. 🤔

To clarify your comment:

If you want a component between your connected one and the one that's run through toJS, then do that. No need to call connect twice:

// Dumb.js
import toJS from './toJS

class Dumb { .. }

export default toJS(Dumb)

-----------------------------------

// Smart.js
import Dumb from './Dumb'

class Smart {
  ..methods..
  render() { return <Dumb {...props} /> }
}

export default connect(mapStateToProps)(Smart)

Upvotes: 1

Related Questions