单击提交按钮后如何重置输入值?

时间:2020-10-12 16:55:17

标签: javascript reactjs redux react-redux

import React, { useState } from 'react';
import './StockQuotes.css'; 
import { createMuiTheme, withStyles, makeStyles, ThemeProvider } from '@material-ui/core/styles';
import Button from '@material-ui/core/Button';
import Table from '@material-ui/core/Table';
import TableBody from '@material-ui/core/TableBody';
import TableCell from '@material-ui/core/TableCell';
import TableContainer from '@material-ui/core/TableContainer';
import TableHead from '@material-ui/core/TableHead';
import TableRow from '@material-ui/core/TableRow';
import Paper from '@material-ui/core/Paper';
import stockData from '../util/stockData';
import { useDispatch } from 'react-redux';
import { getStocksOwned } from '../slices/stocksOwnedSlice';

const StockQuotes = () => {

    const [sharesToBuy, setSharesToBuy] = useState(stockData);
    const dispatch = useDispatch();
    
    const handleChange = (event, index) => {
        stockData[index].owned = parseInt(event.target.value);
        setSharesToBuy(stockData);
    }
    
    const handleClick = (event, index) => {
        event.preventDefault();
        dispatch(getStocksOwned(sharesToBuy));
        stockData[index].owned = 0;
    }

    const StyledTableCell = withStyles((theme) => ({
        head: {
            backgroundColor: theme.palette.common.black,
            color: theme.palette.common.white,
        },
        body: {
            fontSize: 14,
        },
    }))(TableCell);

    const StyledTableRow = withStyles((theme) => ({
        root: {
            '&:nth-of-type(odd)': {
            backgroundColor: theme.palette.action.hover,
            },
        },
    }))(TableRow);

    const useStyles = makeStyles((theme) => ({
        margin: {
            margin: theme.spacing(1),
        },
        table: {
            minWidth: 700,
            },
    }));

    const classes = useStyles();

    const theme = createMuiTheme({
            palette: {
                primary: {main: '#00e676'},
            },
        });

    return(
        <TableContainer component={Paper}>
            <Table className={classes.table} aria-label="customized table">
                <TableHead>
                    <TableRow>
                        <StyledTableCell>Stock Name</StyledTableCell>
                        <StyledTableCell align="right">Current Price</StyledTableCell>
                        <StyledTableCell align="right">Shares</StyledTableCell>
                        <StyledTableCell align="right">Order</StyledTableCell>
                    </TableRow>
                </TableHead>
                <TableBody>
                {stockData.map((stock, index) => (
                    <StyledTableRow key = {index} >
                        <StyledTableCell component="th" scope="row">
                            {stock.name}
                        </StyledTableCell>
                        <StyledTableCell align="right">${stock.price}</StyledTableCell>
                        <StyledTableCell align="right"><input type="number" onChange={event => handleChange(event, index)}></input></StyledTableCell>
                        <StyledTableCell align="right">
                            <ThemeProvider theme={theme}>
                                <Button variant="contained" color="primary" className={classes.margin} onClick={event => handleClick(event, index)}>
                                    BUY
                                </Button>
                            </ThemeProvider>
                        </StyledTableCell>
                    </StyledTableRow>
                    ))}
                </TableBody>
            </Table>
        </TableContainer>
    )
}

export default StockQuotes;

单击提交按钮后,我试图将stockData [index] .owned设置为0。通常很简单,但是这次通过映射包含8个对象的stockData json文件创建了8个输入字段。因此,如果我按输入标记放置value属性然后输入,则整个8个输入字段都会更改。所以我以这种方式编码,除了单击后,我得到“ TypeError:无法分配为只读对象“#”的属性“拥有””。我该怎么办?

3 个答案:

答案 0 :(得分:1)

这里发生了几件事:

  1. stockData似乎只是用于设置组件状态的初始值。状态初始化后,请进行更新并从sharesToBuy中读取,以便这些更改流到您的组件树中。
  2. 请勿更改对象/状态。这可能会导致错误并阻止您的组件正确更新。而是创建一个具有更新值的新副本。
  3. 控制您的input并根据状态更改更新value
// Convenience function for updating state.
const updateOwnedValue = (index, owned) => {
  // Instead of mutating the array/object, create a copy with the updated values
  // and then update state so the changes flow down to your components.
  const newState = sharesToBuy.map((stock, idx) => (
    idx === index ? { ...stock, owned } : stock
  ));

  setSharesToBuy(newState);
};

const handleChange = (event, index) => {
  updateOwnedValue(index, parseInt(event.target.value));
}

const handleClick = (event, index) => {
  event.preventDefault();
  dispatch(getStocksOwned(sharesToBuy));
  updateOwnedValue(index, 0);
}

...
// Iterate over component state instead of the imported value.
{sharesToBuy.map((stock, index) => (
  ...
  <input
    type="number"
    onChange={event => handleChange(event, index)}
    value={
      // This makes your input a controlled component.
      // The value will respond to state changes.
      stock.owned
    }
  />
  ...
))}

答案 1 :(得分:0)

清除输入

为了在单击“提交”时重置输入,您希望输入为controlled component。这意味着从您传递value道具而不是将其存储在本地就可以得到它的价值。然后,您的Submit事件处理程序将该值设置为0。

材料UI支持受控和不受控制的组件,因此这应该是一个容易的更改,但是由于您的应用程序中数据的混乱控制使它变得复杂。此输入值从何而来?要将其设置为0,需要更改哪个值?

数据结构

您需要考虑存在哪些数据以及将其存储在何处。来自json文件的数据应仅用于填充应用程序或其redux存储就是这个的initialState。

现在,您实际上正在突变从json文件导入的stockData值。这真的是一个糟糕的设计。

您可以将某些信息存储在redux中,将某些信息存储在本地组件状态中,但是这两个地方的数据不应相同。本地状态将用于本地更改,例如在点击“购买”按钮之前在表格中输入的数字。而每只股票的当前价格应存储在redux中,并可以通过useSelector访问。

This Revision并不完美,但希望它能使您走上正确的轨道。

嵌套组件

在我上面链接的演示中,您可能会注意到输入字符时输入组件失去了焦点。发生这种情况的原因是,您已在StyledTableCell组件内部定义了组件StyledTableRowStockQuotes。这意味着,每次状态更改时,将重新创建这些组件。该输入不被视为相同的输入,因为它所在的表格单元格是不同的表格单元格,这就是为什么它失去焦点的原因。

您想将样式化的组件定义为StockQuotes组件之外的顶级组件。 (我只是尝试将它们复制并粘贴到外部,但这引入了其他错误。作为一种临时破解解决方案,您可以useMemo避免重新创建,但这并不理想。)

答案 2 :(得分:0)

在handleClick函数中,最后重置您的setSharesToBuy

就像setSharesToBuy('');