Sawtaytoes
9/20/2018 - 4:44 AM

Complex example of ReduxConnection vs connect

A complex example of using connect with its failure situations compared to ReduxConnection:

  • You use mapDispatchToProps.
  • You stack higher-order components such as connect and reduxForm.
  • You have to combine two or more state selectors for one
import CheckCircleIcon from '@material-ui/icons/CheckCircle'
import CircleIcon from '@material-ui/icons/Circle'
import List from '@material-ui/core/List'
import ListItem from '@material-ui/core/ListItem'
import ListItemIcon from '@material-ui/core/ListItemIcon'
import ListItemText from '@material-ui/core/ListItemText'
import PropTypes from 'prop-types'
import React, { Fragment } from 'react'
import { connect } from 'react-redux'
import { push } from 'connected-react-router'
import { withStyles } from '@material-ui/core/styles'

import SectionHeading from '~/components/siteLayout/SectionHeading'
import { listItemsSelector } from '~/redux/lists/selectors'
import { sectionsSelector } from '~/redux/sections/selectors'

const propTypes = {
  namespace: PropTypes.string.isRequired, // Used by Redux `connect` to lookup items
  personLocation: PropTypes.string.isRequired,

  // Material-UI Styles
  classes: PropTypes.object.isRequired,

  // Redux
  listItems: (
    PropTypes
    .arrayOf(
      PropTypes
      .shape({
        id: PropTypes.string.isRequired,
        isChecked: PropTypes.bool,
        name: PropTypes.string.isRequired,
      })
      .isRequired
    )
    .isRequired
  ),
  push: PropTypes.func.isRequired,
  sectionLink: PropTypes.string.isRequired,
  sectionTitle: PropTypes.string.isRequired,
}

export const PersonsList = ({
  classes,
  listItems,
  personLocation,
  push,
  sectionLink,
  sectionTitle,
}) => (
  <Fragment>
    <SectionHeading href={sectionLink}>
      {sectionTitle}
    </SectionHeading>

    <List className={classes.list}>
      {
        listItems
        .map(({
          id,
          isChecked,
          name,
        }) => (
          <ListItem key={id}>
            <ListItemIcon>
              {
                isChecked
                ? (
                  <CheckCircleIcon
                    className={
                      classes
                      .checkmarkIcon
                    }
                  />
                )
                : <CircleIcon />
              }
            </ListItemIcon>

            <ListItemText
              onClick={
                push(
                  personLocation
                  .concat('/')
                  .concat(id)
                )
              }
            >
              {name}
            </ListItemText>
          </ListItem>
        ))
      }
    </List>
  </Fragment>
)

PersonsList
.propTypes = propTypes

const styles = ({ palette }) => ({
  checkmarkIcon: {
    color: palette.primary.main,
  },
  list: {
    backgroundColor: 'white',
    border: `solid 1px ${palette.primary.main}`,
    color: palette.primary.main,
    fontSize: '1em',
    fontWeight: '500',
    height: '1.5em',
    width: '1.5em',
  },
})

const mapStateToProps = (state, { namespace }) => ({
  ...(
    listItemsSelector(
      state,
      { namespace },
    )
  ),
  ...(
    sectionsSelector(
      state,
      { namespace },
    )
  ),
})

const mapDispatchToProps = {
  push,
}

export default (
  connect(
    mapStateToProps,
    mapDispatchToProps,
  )(
    withStyles(
      styles
    )(
      PersonsList
    )
  )
)
import CheckCircleIcon from '@material-ui/icons/CheckCircle'
import CircleIcon from '@material-ui/icons/Circle'
import List from '@material-ui/core/List'
import ListItem from '@material-ui/core/ListItem'
import ListItemIcon from '@material-ui/core/ListItemIcon'
import ListItemText from '@material-ui/core/ListItemText'
import PropTypes from 'prop-types'
import React, { Fragment } from 'react'
import { MaterialUiStyles } from 'imagined-material-ui-styles-component'
import { push } from 'connected-react-router'
import { ReduxConnection } from '@ghadyani-framework/redux-components'

import SectionHeading from '~/components/siteLayout/SectionHeading'
import { listItemsSelector } from '~/redux/lists/selectors'
import { sectionsSelector } from '~/redux/sections/selectors'

const propTypes = {
  namespace: PropTypes.string.isRequired,
  personLocation: PropTypes.string.isRequired,
}

const styles = ({ palette }) => ({
  checkmarkIcon: {
    color: palette.primary.main,
  },
  list: {
    backgroundColor: 'white',
    border: `solid 1px ${palette.primary.main}`,
    color: palette.primary.main,
    fontSize: '1em',
    fontWeight: '500',
    height: '1.5em',
    width: '1.5em',
  },
})

const PersonsList = ({
  namespace,
  personLocation,
}) => (
  <Fragment>
    <ReduxConnection
      namespace={namespace}
      selector={sectionsSelector}
    >
      {({
        sectionLink,
        sectionTitle,
      }) => (
        <SectionHeading href={sectionLink}>
          {sectionTitle}
        </SectionHeading>
      )}
    </ReduxConnection>

    <MaterialUiStyles styles={styles}>
      {({ classes }) => (
        <List className={classes.list}>
          <ReduxConnection
            namespace={namespace}
            selector={listItemsSelector}
          >
            {({ listItems }) => (
              listItems
              .map(({
                id,
                isChecked,
                name,
              }) => (
                <ListItem key={id}>
                  <ListItemIcon>
                    {
                      isChecked
                      ? (
                        <CheckCircleIcon
                          className={
                            classes
                            .checkmarkIcon
                          }
                        />
                      )
                      : <CircleIcon />
                    }
                  </ListItemIcon>

                  <ReduxConnection>
                    {({ dispatch }) => (
                      <ListItemText
                        onClick={
                          dispatch(
                            push(
                              personLocation
                              .concat('/')
                              .concat(id)
                            )
                          )
                        }
                      >
                        {name}
                      </ListItemText>
                    )}
                  </ReduxConnection>
                </ListItem>
              ))
            )}
          </ReduxConnection>
        </List>
      )}
    </MaterialUiStyles>
  </Fragment>
)

PersonsList
.propTypes = propTypes

export default PersonsList