【问题标题】:React - Clear form after submit with Context API state managementReact - 使用 Context API 状态管理提交后清除表单
【发布时间】:2020-07-04 10:27:29
【问题描述】:

我有一个简单的 Expense Tracker,它带有一个表单,可以将事务添加到 mongoDB 后端。我正在使用上下文 API 和材质 UI 表单子组件。我一生都无法弄清楚如何在 onSubmit 被触发后清除文本字段,但总的来说我对 React 和 Javascript 还是很陌生。我看过其他一些关于使用按钮创建一个作为 onClick 触发的函数的帖子,但我不确定这是正确的路线,因为我的全局上下文中有一个“initialState”。我还看到了使用 appReducer 执行此操作的其他路线,但我不知道如何应用它。需要明确的是,我希望它清除提交时的表单,而不是单击“清除字段”的按钮。以下是我的代码:

表单组件

import React, { useState, useContext } from "react";
import { GlobalContext } from "../context/GlobalState";

// UI for Text Field
import { makeStyles } from "@material-ui/core/styles";
import TextField from "@material-ui/core/TextField";
import Button from "@material-ui/core/Button";
import Grid from "@material-ui/core/Grid";

// UI and utils for date picker
import "date-fns";
import DateFnsUtils from "@date-io/date-fns";
import {
  MuiPickersUtilsProvider,
  KeyboardDatePicker
} from "@material-ui/pickers";

const useStyles = makeStyles(theme => ({
  root: {
    "& > *": {
      align: "center",
      margin: theme.spacing(1),
      width: 200,
      flexgrow: 1
    }
  },
  textfield: {
    height: 38
  },
  button: {
    height: 38,
    align: "center"
  },
  grid: {
    fullwidth: true,
    direction: "row",
    justify: "center",
    alignItems: "center",
    display: "flex",
    flexDirection: "column",
    justifyContent: "center"
  }
}));

export const AddTransaction = () => {
  const classes = useStyles();

  const [transactionDate, setTransactionDate] = useState(new Date());
  const [text, setText] = useState('');
  const [amount, setAmount] = useState(0);

  const { addTransaction } = useContext(GlobalContext);


  const onSubmit = e => {
    e.preventDefault();

    const newTransaction = {
      transactionDate,
      text,
      amount: +amount
    };

    addTransaction(newTransaction);
  };

  return (
    <React.Fragment>
      <h3 align="center">Add new transaction</h3>
      <Grid container className={classes.grid}>
        <form
          className={classes.root}
          noValidate
          autoComplete="off"
          onSubmit={onSubmit}
        >
          <MuiPickersUtilsProvider utils={DateFnsUtils}>
            <KeyboardDatePicker
              disableToolbar
              variant="inline"
              format="MM/dd/yyyy"
              margin="normal"
              id="Transaction Date"
              label="Transaction Date"
              onChange={e => setTransactionDate(e)}
              value={transactionDate}
              KeyboardButtonProps={{
                "aria-label": "change date"
              }}
            />
          </MuiPickersUtilsProvider>
          <TextField
            className={classes.textfield}
            id="Transaction Name"
            label="Transaction Name"
            variant="outlined"
            size="small"
            type="text"
            margin="dense"
            onChange={e => setText(e.target.value)}
            value={text}
            required = {true}
          />
          <TextField
            className={classes.textfield}
            id="Amount"
            label="Amount"
            variant="outlined"
            size="small"
            type="number"
            margin="dense"
            onChange={e => setAmount(e.target.value)}
            value={amount}
            required = {true}
          />
            <Button
              className={classes.button}
              variant="contained"
              color="primary"
              type="submit"
              fullwidth
            >
              Add transaction
            </Button>
        </form>
      </Grid>
    </React.Fragment>
  );
};

全球状态

import React, { createContext, useReducer } from 'react';
import AppReducer from './AppReducer';
import axios from 'axios';

// Initial state
const initialState = {
  transactions: [],
  error: null,
  loading: true
}

// Create context
export const GlobalContext = createContext(initialState);

// Provider component
export const GlobalProvider = ({ children }) => {
  const [state, dispatch] = useReducer(AppReducer, initialState);

  // Actions
  async function getTransactions() {
    try {
      const res = await axios.get('/api/v1/transactions');

      dispatch({
        type: 'GET_TRANSACTIONS',
        payload: res.data.data
      });
    } catch (err) {
      dispatch({
        type: 'TRANSACTION_ERROR',
        payload: err.response.data.error
      });
    }
  }

  async function deleteTransaction(id) {
    try {
      await axios.delete(`/api/v1/transactions/${id}`);

      dispatch({
        type: 'DELETE_TRANSACTION',
        payload: id
      });
    } catch (err) {
      dispatch({
        type: 'TRANSACTION_ERROR',
        payload: err.response.data.error
      });
    }
  }

  async function addTransaction(transaction) {
    const config = {
      headers: {
        'Content-Type': 'application/json'
      }
    }

    try {
      const res = await axios.post('/api/v1/transactions', transaction, config);
      dispatch({
        type: 'ADD_TRANSACTION',
        payload: res.data.data
      });
    } catch (err) {
      dispatch({
        type: 'TRANSACTION_ERROR',
        payload: err.response.data.error
      });
    }
  }

  return (<GlobalContext.Provider value={{
    transactions: state.transactions,
    error: state.error,
    loading: state.loading,
    getTransactions,
    deleteTransaction,
    addTransaction
  }}>
    {children}
  </GlobalContext.Provider>);
}

应用程序缩减器

export default (state, action) => {
  switch(action.type) {
    case 'GET_TRANSACTIONS':
      return {
        ...state,
        loading: false,
        transactions: action.payload
      }
    case 'DELETE_TRANSACTION':
      return {
        ...state,
        transactions: state.transactions.filter(transaction => transaction._id !== action.payload)
      }
    case 'ADD_TRANSACTION':
      return {
        ...state,
        transactions: [...state.transactions, action.payload],
      }
    case 'TRANSACTION_ERROR':
      return {
        ...state,
        error: action.payload
      }
    default:
      return state;
  }
}

【问题讨论】:

    标签: reactjs forms material-ui react-state-management context-api


    【解决方案1】:

    在我兄弟的帮助下,我终于能够解决这个问题。他建议将表单的私有状态与我的全局状态分开管理并不理想,但这完成了工作。我在 addTransaction 调用之后重置了表单状态。

    表单组件

    //No changes to import or styling //
    
    export const AddTransaction = () => {
      const classes = useStyles();
    
      const [transactionDate, setTransactionDate] = useState(new Date());
      const [text, setText] = useState('');
      const [amount, setAmount] = useState(0);
    
      const { addTransaction } = useContext(GlobalContext);
    
    
      const onSubmit = e => {
        e.preventDefault();
    
        const newTransaction = {
          transactionDate,
          text,
          amount: +amount
        };
    
        addTransaction(newTransaction);
    
        // Here is what I've added that will return the form to it's original state
    
        setText('') //this resets the textfield to an empty string
        setAmount(0) //same as above but 0 amount
    
      };
    
    //no changes to return//
    

    【讨论】:

      【解决方案2】:

      您好,感谢您提出问题。

      我解决这个问题的方法是对输入字段使用 usestate 钩子,然后使用 onCLick 将其设置为零。 这是一个例子:

      假设我的网站上有一个名为 search 的输入字段,它连接到 google 搜索。这意味着我网站上的搜索栏调用 google 上的搜索栏。一旦用户输入一个值并单击搜索,我将调用 setSearch() 到一个新值,这是用户的输入。

      const [search, setSearch] = useState(""); // for user input
      
      //once the user inputs a value and clicks a button I will call **two functions** 
      
      //The first fires off a google search
      const searchRequest = () => {
          window.open(`https://www.google.com/search?q=${search}`, `_blank`);
        };
      
      // the second function does what you want, it sets the value of search to an empty string.
      
      <Button variant="outlined" size="large" 
      onClick={(e) => {
      searchRequest(e);
      setSearch(" ")}} // <-- I think this solves your problem, if you follow my logic.
      >
        Search
       </Button>
      

      希望这会有所帮助,如果需要更清楚,请告诉我。

      【讨论】:

      • 嗨@nadim95,感谢您的回复。虽然这在实践中是有意义的,但由于我有 2 个文本框和一个日期选择器,我有 3 个状态要在表单上更新(setTransactionDate、setAmount 和 setText)。我尝试了一个函数来更新 addTransaction 函数之后的所有 3 个函数,但是我从后端收到“错误请求错误”。好像我需要触发 addTransaction,然后是一个 clearForm 函数来重新呈现默认表单状态而不对后端进行任何更改
      • 一个清除函数,在前端将所有三个状态值设置为一个空字符串。数据提交到后端后,需要清除字段正确吗?
      • 是的,但只有一个是字符串,其他是日期和金额。我正在使用 React Context 中的 reducer 管理我的状态,所以这就是挑战所在。我一直在尝试返回到 initialState,但没有找到太多运气。
      • 我会更深入地研究它,但是您可以在本地存储中声明所有三种状态,并在用户将数据提交到后端。这意味着 AddTransaction 组件将需要从 useContext 导入状态。以及触发 API 请求的组件需要导入 clear 函数。我会更多地研究它,但希望这会有所帮助。
      猜你喜欢
      • 2020-08-14
      • 1970-01-01
      • 2020-03-29
      • 1970-01-01
      • 2021-09-24
      • 2020-11-11
      • 2022-11-01
      • 2018-03-14
      • 2012-01-31
      相关资源
      最近更新 更多