reactjs – How do I programmatically create an element on a page in Meteor?

Question:

Hello! My question is very simple, and it touches on a basic thing – rendering elements on a page. But to my surprise, I have not been able to find information on this issue for a long time.

I am using ReactJS as a front-end framework.

Let's say I have a page with a simple form: a field, an Add Field button, and a Submit button. Attention question: how to implement this same Add field button? When you click on it, another text field appears.

I'll tell you how I tried to solve this problem.

class IngredientForm extends Component {
  render() {
    return(

      <form onSubmit={this.handleSubmit.bind(this)}>

        <input type="text"/>

        { this.renderOtherInputs() }

        <input type="button" value="Add Ingredient" onClick={this.addIngredient.bind(this)}>

        <input type="submit" value="Submit Form">

      </form>
    );
  }
}

Here is the code for this form itself. When you click on the Add Ingredient button, another <input type="text> should appear.

How can this be done? I know that Meteor is based on reactivity, so there is no need to directly render anything. You need to rely on some reactive data source and modify it. And it will already cause changes to the interface.

In the tutorials, only one situation was considered – we have a collection, we need to display each document from the collection. That is, a reactive data source is a collection, we fold it into an array, and then draw an interface for each element of the array. Well, I decided to do the same, but since the number of inputs on the page is a local state on the client, it would be wrong to store it in the collection. Meteor has an answer – reactive var. we have:

numOfIngredients = new ReactiveVar([]);

When we click on Add field, the following method works:

addIngredient(e) {
    e.preventDefault();
    let newNumOfIngredients = numOfIngredients.get();
    newNumOfIngredients.push('no matter');
    numOfIngredients.set(newNumOfIngredients);
}

Well, and the method for rendering based on our reactive array:

renderOtherInputs() {
  return numOfIngredients.get().map((elem) => {
    return(
      <input type="text"/>
    );
  }
}

But the code doesn't work. I registered the console.log(numOfIngredients.get()) method in the addIngredient method and when I clicked on the add field button, the correct behavior was displayed in the console:

['no matter']
['no matter','no matter']
['no matter','no matter','no matter']
...

However, the box does not appear. I tried and do with createContainer by declaring numOfIngredients = new ReactiveVar([]); there and then referring to it as this.props.numOfIngredients.get() . The array is filled, printed to the console, inputs do not appear. Perhaps my decision is inherently wrong and crunchy.

question in english

I really hope for the help of the community, especially since my question is elementary, as it seems to me. Thanks in advance for your answers!

Answer:

The problem was solved.

So. It was absolutely correct to assume that reactivity is the key to solving the problem. But whose reactivity? I naively assumed it was a meteor, and therefore used his Reactive Var tool. And here lies the mistake! ReactJS has its own facility for making reactive changes and (apparently) is not at all friendly with Reactive Var's and ReactiveDict's.

You need to use the state of the React component. State is essentially an object, so let's name the field whatever we like and store the data type we need, in our case = array. You need to set the initial state value in the constructor of the component class:

constructor(props) {
  super(props);
  this.state = {
    inputs: [],
  }
}

We have set the Inputs field, which will be an empty array at the time the component is rendered.

Now let's write a function for drawing additional inputs, relying on state.inputs:

renderOtherInputs() {
  return this.state.inputs.map( (each, index) => {
    return (
      <input key={ index } type="text" />
    );
  });
}

And let's write a function to add a new field. So that when a new element is added to the array, a new input is immediately drawn on the screen, we need to interact with state.inputs not directly, but using the setState function, which provides reactivity:

addInput(e) {
  e.preventDefault();
  var temp = this.state.inputs;
  temp.push('no matter');
  this.setState({
    inputs: temp,
  });
}

Ready!

ps I'm really upset that I didn't get an answer. Not ready-made, but at least a monosyllabic tip: state. In all the docks I read that the meteor, and even react in particular, on stackoverflow have a huge community .. and here's to you, with an elementary question to help laziness

p.c. 2. I fought for half the evening and wrote a function to remove a specific input:

deleteIngredient(e) {
  e.preventDefault();
  let index = e.target.getAttribute('id');
  let temp = this.state.inputs;
  delete temp[index];
  this.setState({
    inputs: temp,
  });
}

Know that delete creates a hole in the array, but using temp.splice(index,1) doesn't work. The input is removed, but always only the last one from the bottom. Given that the required element is removed from the array.

Next to the input, respectively, you need a button with onClick= {this.deleteIngredient.bind(this)}

p. 3 to figure it out by yourself, of course it's nice 🙂 and I hope at least someone helped

Scroll to Top