javascript - Material-UI 复选框不适用于 Redux 商店

标签 javascript reactjs redux material-ui

The source can be accessed in this repo

我使用 Redux 存储来更新复选框的检查标志,我可以看到状态正在完全改变,但事情并没有应用于 React 组件。

我认为一切都很好,但是当我更改状态并加上检查时,复选框不会更新。

  • 使用的Redux存储位于src/redux/modules/menu.js中,与复选框相关的操作创建者是checkMenuNameList函数。

  • 此外,复选框代码可以在 src/containers/MenuEditContainer.js 中找到。

import React from 'react'
import { connect } from 'react-redux'
import { bindActionCreators } from 'redux'

import * as actions from '../redux/modules/menu'

import PageWrapper from '../components/base/PageWrapper'
import MenuEditWrapper from '../components/common/templates/MenuEditWrapper'
import MenuEditContent from '../components/common/content/MenuEditContent'

const MenuEditContainer = (props) => {
  const handleInputChange = name => event => {
    props.actions.changeInput({
      key: name,
      value: event.target.value
    })
    props.actions.generateMenuList(event.target.value)
  }

  const handleMenuNameCheckbox = index => event => {
    props.actions.checkMenuNameList(index)
  }

  return (
    <>
      <PageWrapper>
        <MenuEditWrapper>
          <MenuEditContent
            menuName={props.menuName}
            menuPrice={props.menuPrice}
            menuNameList={props.menuNameList}
            handleInputChange={handleInputChange}
            handleMenuNameCheckbox={handleMenuNameCheckbox}
          />
        </MenuEditWrapper>
      </PageWrapper>
    </>
  )
}

const mapStateToProps = ({ menu }) => ({
  menuId: menu.menuId,
  menuName: menu.menuName,
  menuPrice: menu.menuPrice,
  menuNameList: menu.menuNameList,
  menuNameListChosen: menu.menuNameListChosen,
})

const mapDispatchToProps = (dispatch) => ({
  actions: bindActionCreators(actions, dispatch)
})

export default connect(
  mapStateToProps,
  mapDispatchToProps
)(MenuEditContainer)

import React from 'react'
import { withRouter } from 'react-router-dom'

import Paper from '@material-ui/core/Paper'
import Typography from '@material-ui/core/Typography'
import TextField from '@material-ui/core/TextField'

import FormGroup from '@material-ui/core/FormGroup'
import FormControlLabel from '@material-ui/core/FormControlLabel'
import Checkbox from '@material-ui/core/Checkbox'

import PageTitle from '../../typography/PageTitle'
import AddButtonSet from '../../button/AddButtonSet'
import EditButtonSet from '../../button/EditButtonSet'

import { makeStyles } from '@material-ui/core/styles'
const useStyles = makeStyles(theme => ({
  root: {
    padding: theme.spacing(3, 2)
  },
  title: {
    fontWeight: 200,
    marginBottom: '1.5rem'
  },
  textField: {
    marginLeft: theme.spacing(0.5),
    marginRight: theme.spacing(1),
    width: 200,
    display: 'block'
  },
}))

const MenuEditContent = (props) => {
  const classes = useStyles()
  const { match, location, history } = props
  return (
    <Paper className={classes.root}>
      <PageTitle>
        {
        (location.pathname === '/menu/edit/new')
        ? (`새로운 메뉴 등록`)
          // 기존 메뉴 수정시
        : (match.params.menuId)
          ? (`메뉴 수정`)
          : (``)
        }
      </PageTitle>
      <TextField
        id="menuName"
        label="메뉴 이름"
        type="search"
        className={classes.textField}
        margin="normal"
        variant="outlined"
        autoComplete="off"
        value={props.menuName}
        onChange={props.handleInputChange('menuName')}
      />
      <TextField
        id="menuPrice"
        label="가격"
        type="search"
        className={classes.textField}
        margin="normal"
        variant="outlined"
        autoComplete="off"
        value={props.menuPrice}
        onChange={props.handleInputChange('menuPrice')}
      />
      {
        (props.menuNameList.length > 0) && (
          <FormGroup>
          {
            props.menuNameList.map((item, index) => (
              <FormControlLabel
                key={index}
                control={
                  <Checkbox
                    checked={item.checked}
                    value={index}
                    onChange={props.handleMenuNameCheckbox(index)}
                  />
                }
                label={item.name}
              />
            ))
          }
          </FormGroup>
        )  
      }
      {
        (location.pathname === '/menu/edit/new')
        ? (
            <AddButtonSet
              onClickCreate={() => alert('create button!')}
              onClickCancel={() => history.push('/menu')}
            />
          )
        : (match.params.menuId)
          ? (
            <EditButtonSet
              onClickUpdate={() => alert('update button!')}
              onClickDelete={() => alert('delete button!')}
              onClickCancel={() => history.push('/menu')}
            />
          )
          : (<></>)
      }
    </Paper>
  )
}

export default withRouter(MenuEditContent)

最佳答案

我的复选框不起作用的原因是我错误地重新使用了之前的状态。

// redux/modules/menu.js
const updateMenuNameList = createAction(UPDATE_MENU_NAME_LIST, payload => ({ menuNameList: payload.menuNameList }))

export const checkMenuNameList = (index) => (dispatch, getState) => {
  const { menu: { menuNameList } } = getState()
  const previousStatus = menuNameList[index].checked
  menuNameList[index].checked = !previousStatus
  dispatch(onCheckMenuNameList({ updatedMenuNameList: menuNameList }))
}

在上面的示例中,我从 getState() 获取了先前的状态,并从中提取了 menuNameListmenuNameList 的结构如下所示:

[
  {
    name: String,
    checked: Boolean
  }
]

每个复选框都使用此数组来显示名称并确定是否选中。当我单击任何复选框时,处理程序将更改单击的复选框的 checked 值。

问题从这里出现:我不小心重复使用了之前的 menuNameList 中的对象,并且仅在更改了某些 checked 值的情况下更新了它。这不是正确的方法,因为即使内部属性发生变化,Redux 或 React 也不知道发生了什么变化! React 和 Redux 通过对象的浅层比较来计算状态变化。因此,即使存储发生变化,React 也不会渲染 View ,并且更改不会应用到 View !

为了避免这个问题,我们应该为menuNameList创建一个新对象。也许建议使用像 Immutable.js 这样的库。

// redux/modules/menu.js
export const checkMenuNameList = (idx) => (dispatch, getState) => {
  const { menu: { menuNameList } } = getState()

  // deep copy
  const newMenuNameList = menuNameList.map((item, index) => {
    if (index !== idx) {
      const newMenu = {
        name: item.name,
        checked: item.checked
      }
      return newMenu
    } else {
      const newMenu = {
        name: item.name,
        checked: !item.checked
      }
      return newMenu
    }
  })

  dispatch(onCheckMenuNameList({ updatedMenuNameList: newMenuNameList }))
}

关于javascript - Material-UI 复选框不适用于 Redux 商店,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/59197605/

相关文章:

redux 中的 reducer 中的验证

javascript - Angular ngrx : TypeError: Cannot freeze array buffer views with elements

javascript - JavaScript 'for' 循环中的执行顺序是什么?

javascript - 拆分字符串并获取最长字符串的最大长度

javascript - React Redux 更新项数量(不止一个增量)

javascript - 函数是否作为 props 在此代码中传递?

javascript - 为什么 Google map 不重新加载新坐标和列表的其余部分详细信息?

javascript - 将 redux 客户端从 REST 转换为 GraphQL Apollo?

javascript - 揭示模块模式问题

javascript - 你能在Javascript中动态创建一个数组吗?