最新消息:雨落星辰是一个专注网站SEO优化、网站SEO诊断、搜索引擎研究、网络营销推广、网站策划运营及站长类的自媒体原创博客

javascript - How to clear input values of dynamic form in react - Stack Overflow

programmeradmin4浏览0评论

I have a dynamic form as a functional component which is generated via a class based component. I want to make reset button which clears the input field values and sets the state to null array.

Full code is available here:

I want to make a reset button, clearing all the input values and initializing the Itemvalues array to null. Even if I set the values to null, it doesn't clear the input field.

However, the problem I'm facing is that since, it is a dynamic form and a functional component it doesn't have a predefined state for each individual form field making it difficult to set value to null. Can someone please help, I'm stuck on this from a long time

I have a dynamic form as a functional component which is generated via a class based component. I want to make reset button which clears the input field values and sets the state to null array.

Full code is available here: https://codesandbox.io/s/beautiful-archimedes-o1ygt

I want to make a reset button, clearing all the input values and initializing the Itemvalues array to null. Even if I set the values to null, it doesn't clear the input field.

However, the problem I'm facing is that since, it is a dynamic form and a functional component it doesn't have a predefined state for each individual form field making it difficult to set value to null. Can someone please help, I'm stuck on this from a long time

Share Improve this question edited Jun 25, 2019 at 3:32 user8306074 asked Jun 23, 2019 at 13:13 user8306074user8306074 3954 gold badges9 silver badges20 bronze badges 7
  • Do you want to keep the fields but reset the value ? How the objects inside the Items array looks like ? – Asaf Aviv Commented Jun 23, 2019 at 13:43
  • Your code is unreadable and got syntax errors, fix it. – Dennis Vash Commented Jun 23, 2019 at 13:43
  • How are the items generated to begin with? – Cat_Enthusiast Commented Jun 26, 2019 at 1:57
  • In the sample code, I've generated them manually, but in the actual code the array is filled from API – user8306074 Commented Jun 26, 2019 at 3:57
  • @user8306074 gotcha, working on something for you. – Cat_Enthusiast Commented Jun 26, 2019 at 4:26
 |  Show 2 more comments

4 Answers 4

Reset to default 10 +50

Here's a codesandbox to show you how to reset the items: https://codesandbox.io/s/romantic-heisenberg-93qi7

I also left a note for you on how to get this to work with your API data, see the comment inside onChangeText()

The problem is that the inputs are not controlled by state as you have deduced. We should create an updated object for each item from your API, giving it a value prop.

index.js

import React from "react";
import ReactDOM from "react-dom";
import Cart from "./Cart";

import "./styles.css";

class App extends React.Component {
  constructor(props) {
    super(props);
    this.state = {
      Items: [],
      itemvalues: [{}]
    };
    this.onChangeText = this.onChangeText.bind(this);
    this.getItems = this.getItems.bind(this);
    this.handleReset = this.handleReset.bind(this);
    this.handleSubmit = this.handleSubmit.bind(this);
    this.findFieldIndex = this.findFieldIndex.bind(this);
    this.trimText = this.trimText.bind(this);
  }

  getItems = () => {
    /*if the data is coming from an API, store it in an array then .map() over it.
     we can add a value prop to the object like:
      so you can do something like:

      const newItems = [...apiData].map((item) => {
        return {
          ...item,
          value: ""
        }
      })

      this.setState({
        Items: newItems
      })
     */

    this.setState({
      Items: [
        {
          name: "item1",
          description: "item1",
          group: "groupA",
          dtype: "str",
          value: ""
        },
        {
          name: "item2",
          description: "item2",
          group: "groupA",
          dtype: "str",
          value: ""
        },
        {
          name: "item3",
          description: "item3",
          group: "groupB",
          dtype: "str",
          value: ""
        },
        {
          name: "item4",
          description: "item4",
          group: "groupB",
          dtype: "str",
          value: ""
        }
      ]
    });
  };

  onChangeText = e => {
    const updatedItems = [...this.state.Items].map(item => {
      if (item.name === e.target.name) {
        return {
          ...item,
          value: e.target.value
        };
      } else {
        return item;
      }
    });

    const updatedItemValues = [...updatedItems].reduce((obj, curr) => {
      if (!obj[curr.group]) {
        obj[curr.group] = [];
      }
      obj[curr.group] = [...obj[curr.group], { [curr.name]: curr.value }];
      return obj;
    }, {});

    this.setState({
      ...this.state,
      Items: updatedItems,
      itemvalues: updatedItemValues
    });
  };

  findFieldIndex = (array, name) => {
    return array.findIndex(item => item[name] !== undefined);
  };
  trimText(str) {
    return str.trim();
  }

  handleReset = () => {
    const resetedItems = [...this.state.Items].map(item => {
      return {
        ...item,
        value: ""
      };
    });

    this.setState(
      {
        ...this.state,
        Items: resetedItems,
        itemvalues: []
      },
      () => console.log(this.state)
    );
  };

  handleSubmit = () => {
    console.log(this.state.itemvalues);
  };

  render() {
    return (
      <div>
        {
          <Cart
            Items={this.state.Items}
            getItems={this.getItems}
            handleSubmit={this.handleSubmit}
            handleReset={this.handleReset}
            onChangeText={this.onChangeText}
          />
        }
      </div>
    );
  }
}

Cart.js

import React, { useEffect } from "react";
import Form from "./Form";

const Cart = props => {
  useEffect(() => {
    props.getItems(props.Items);
  }, []);

  return (
    <div>
      <Form Items={props.Items} onChangeText={props.onChangeText} />

      <button onClick={props.handleSubmit}>Submit</button>
      <button onClick={props.handleReset}>Reset</button>
    </div>
  );
};

export default Cart;

The Cart component can remain mostly the same, we do not need to pass in props.items to useEffect() dependency.

Form.js

import React from "react";

const Form = props => {
  return (
    <div>
      {props.Items.map(item => {
        return (
          <input
            name={item.name}
            placeholder={item.description}
            data-type={item.dtype}
            data-group={item.group}
            onChange={e => props.onChangeText(e)}
            value={item.value}
          />
        );
      })}
    </div>
  );
};
export default Form;

Now in Form component, we provide each input a value prop that is connected to the item our upper-most parent component-state.

That's pretty much all you need to reset the values.

See if that works for you:

Working example on CodeSandbox

Since you were already using hooks in part of your code, I've converted your class into a functional component using hooks (my advice: learn hooks and forget about class components).

I've added a value property to your INITIAL_STATE so it will keep the input value for each inputItem.

Full CODE:

index.js

import React, { useState } from "react";
import ReactDOM from "react-dom";
import FormV2 from "./FormV2";

import "./styles.css";

function App() {
  const INITIAL_STATE = [
    {
      name: "item1",
      description: "item1",
      group: "groupA",
      dtype: "str",
      value: "" // ADDED VALUE PROPERTY TO KEEP THE INPUT VALUE
    },
    {
      name: "item2",
      description: "item2",
      group: "groupA",
      dtype: "str",
      value: ""
    },
    {
      name: "item3",
      description: "item3",
      group: "groupB",
      dtype: "str",
      value: ""
    },
    {
      name: "item4",
      description: "item4",
      group: "groupB",
      dtype: "str",
      value: ""
    }
  ];

  const [inputItems, setInputItems] = useState(INITIAL_STATE);

  function handleChange(event, index) {
    const newValue = event.target.value;
    setInputItems(prevState => {
      const aux = Array.from(prevState);
      aux[index].value = newValue;
      return aux;
    });
  }

  function handleReset() {
    console.log("Reseting Form to INITIAL_STATE ...");
    setInputItems(INITIAL_STATE);
  }

  function handleSubmit() {
    inputItems.forEach(item =>
      console.log(
        "I will submit input: " + item.name + ", which value is: " + item.value
      )
    );
  }

  return (
    <FormV2
      handleSubmit={handleSubmit}
      handleReset={handleReset}
      handleChange={handleChange}
      inputItems={inputItems}
    />
  );
}

ReactDOM.render(<App />, document.getElementById("root"));

FormV2.js

import React from "react";

function FormV2(props) {
  const formInputItems = props.inputItems.map((item, index) => (
    <div key={item.name}>
      {item.name + ": "}
      <input
        type="text"
        data-type={item.dtype}
        data-group={item.group}
        placeholder={item.description}
        value={item.value}
        onChange={event => props.handleChange(event, index)}
      />
    </div>
  ));

  return (
    <React.Fragment>
      <form>{formInputItems}</form>
      <button onClick={props.handleSubmit}>Submit</button>
      <button onClick={props.handleReset}>Reset</button>
      <div>State: {JSON.stringify(props.inputItems)}</div>
    </React.Fragment>
  );
}

export default FormV2;

In order to control the values of the child components (Items) which I presume are input fields you need to be passing down their values from their parent component. So each of your items will have an item.value which is stored in the parent component's state.

That means that in the parent component you will be able to define a method which clears all of the item values it is storing in its state. That will probably look something like

resetInputs = () => {
    this.setState({
        inputFields: this.state.inputFields.map(inputField => {
            ...inputField,
            value: ''
        }
    })
}

Also you'll need to write what kind of tag you want for your code to work, like input.

So what you'll end up with for the code of the child component you shared is something like:

const Form = (props) => {
    return (
    <div>
        {props.Items.map(item => (
          <input
            name={item.name}
            value={item.value}
            placeholder={item.description}
            onChange={e => props.onChangeText(e)}
          /> 
        )
        )}
      </div> 
    );
}
export default Form 

You want to manage the state of unknown number N of items, one way to achieve it is by managing a single object which contains all states, for example, setValuesManager manages N inputs and clicking the button reset its state:

function TextAreaManager() {
  const [valuesManager, setValuesManager] = useState([...items]);
  return (
    <Flexbox>
      {valuesManager.map((value, i) => (
        <TextBoxItem
          key={i}
          value={value}
          onChange={e => {
            valuesManager[i] = e.target.value;
            setValuesManager([...valuesManager]);
          }}
        />
      ))}
      <PinkButton
        onClick={() =>
          setValuesManager([...Array(valuesManager.length).fill('')])
        }
      >
        Reset All
      </PinkButton>
    </Flexbox>
  );
}

Demo:

发布评论

评论列表(0)

  1. 暂无评论