[Functional Programming + React] Provide a reasonable default value for mapStateToProps in case initial state is undefined

For example we have a component, it needs to call 'react-redux' connect function.

import { compose, curry, option, propPath } from '../js/helper'

const FilterButton = ({ active, onClick }) => {
    const classes = classnames('filterButton', {
        'filterButton--active': active
    })
    return <Button className={classes} onClick={onClick} icon={faFilter} />
}

FilterButton.defaultProps = {
    active: true,
    onClick: Function.prototype
}

FilterButton.propTypes = {
    active: PropTypes.bool,
    group: PropTypes.string.isRequired,
    onClick: PropTypes.func
}

const mapStateToProps = (state, ownProps) => ({
    active: state.ui.filterGroups[ownProps.group]
})

export default connect(mapStateToProps)(FilterButton)

For the hightlighted part, there can be many possible reason for it to go wrong. We can use Maybe to provide a reasonable default value.

First, the inital State is:

const data = {
    nextId: 4,
    todoFilter: 'SHOW_ALL',
    todos: [
       ...
    ],
    ui: {
        filterGroups: {
            status: false
        }
    }
}

If the highlighted part is undefined, we still want it works.

import { chain, compose, curry, isBoolean, option, propPath, safe } from 'crocks'
...

const activeGroup = curry(group =>
    compose(
        option(FilterButton.defaultProps.active),
chain(safe(isBoolean)), propPath([
'ui', 'filterGroups', group]) ) ) const mapStateToProps = (state, ownProps) => ({ active: activeGroup(ownProps.group, state) }) export default connect(mapStateToProps)(FilterButton)

The reason to put a chain() there is because, if the initial state is not boolean but string value, we can do safe type check, to make sure, what we got is Boolean value in the end, it not a Boolean, then Option() will set it to false. 

--Full code--

import React from 'react'
import PropTypes from 'prop-types'
import { connect } from 'react-redux'
import Button from './controls/Button'
import classnames from 'classnames'
import { faFilter } from '@fortawesome/free-solid-svg-icons'

import { chain, compose, curry, isBoolean, option, propPath, safe } from 'crocks'

const FilterButton = ({ active, onClick }) => {
    const classes = classnames('filterButton', {
        'filterButton--active': active
    })
    return <Button className={classes} onClick={onClick} icon={faFilter} />
}

FilterButton.defaultProps = {
    active: true,
    onClick: Function.prototype
}

FilterButton.propTypes = {
    active: PropTypes.bool,
    group: PropTypes.string.isRequired,
    onClick: PropTypes.func
}

const activeGroup = curry(group =>
    compose(
        option(FilterButton.defaultProps.active),
chain(safe(isBoolean)), propPath([
'ui', 'filterGroups', group]) ) ) const mapStateToProps = (state, ownProps) => ({ active: activeGroup(ownProps.group, state) }) export default connect(mapStateToProps)(FilterButton)
原文地址:https://www.cnblogs.com/Answer1215/p/10727119.html