React - 使用 Context API 状态管理提交后清除表单

React - Clear form after submit with Context API state management

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

表单组件

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;
  }
}

您好,感谢您提出问题。

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

假设我的网站上有一个名为搜索的输入字段,它连接到 google 搜索。这意味着我网站上的搜索栏调用 google.Once 上的搜索栏,用户输入一个值并单击搜索我将调用 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>

希望这对您有所帮助,如果我需要更清楚地说明,请告诉我。

终于在哥哥的帮助下解决了这个问题。他建议将表单的私有状态与我的全局状态分开管理并不理想,但这完成了工作。我在 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//