skip to Main Content

I’m new to meteor and react. Let’s say I have this in my react component:

getMeteorData() {
    var myForm = ProjectForm.findOne({_id:this.props.router.params._id});
    var projects = ProjectForm.find({});
    return {myForm:myForm,projects:projects};
    // doing a console.log(myForm); would give you something like
    /*
    input1:my text 1
    input2:some other text
    input3:something else
    etc....
    */
  },
  renderListProjects() {

    return this.data.projects.map(function(projectform,i) {
      return <li key={"li"+i}><a href={Meteor.absoluteUrl()+'project/' + projectform.username +'/' +projectform._id} key={"a"+i}>Project {projectform._id}</a></li>;
    });
  },
  getInitialState() {
     return Projects.findOne({this.props.router.params._id}, {sort: {createdAt: -1}});
  },
  render() {
    return (
      <div>
      <ul>{this.renderListProjects()}</ul>
      <form>
         <span>Hello this is some text</span>
         <input type="text" ref="input1" />
         <p>Blah blah this is boring</p>
         <input type="text" ref="input2" />
         <img src="image-of-a-kangaroo.png" />
         <input type="text" ref="input3" />
         <ul>
            <li>Buy brocolli</li>
            <li>Buy oregano</li>
            <li>Buy milk</li>
         </ul>
         <input type="text" ref="input4" />
         ...
         <textarea ref="input100"></textarea>
         <input type="text" ref="input101" />
         <p><strong>Yes, I like pizza!</strong>  But my porcupine gets sick eating pizza.</p>
         ...
      </form>
      </div>
    );

What I want to do is assign the values of this.data.myForm to each of the form fields in the render() function. But when I do something like <input type="text" ref="input1" value={this.data.myForm.input1} />, and I go to my web browser and put my cursor on that field, I am NOT able to modify the value. Pressing the keys on my keyboard will not change the value of that input field. Additionally, I have about 250 input fields in this html form. I really don’t want to do any data entry. I would much rather just use some kind of loop to iterate through the this.data.myForm and assign it to the corresponding form fields. But when I tried to do this, I get problems about the DOM not being found or it’s not loaded. So I tried writing some code in componentDidMount(), but then I got other errors.

Can anyone point me in the right direction on how to efficiently bind all my this.data.myForm data to my form fields AND allow me to edit the form fields after?

Additional Requirements

  1. If someone clicks on link1 from the renderListProjects then clicks on link2 from the renderlistProjects, then the form must show the value of link2’s projectform._id.

  2. In the DOM, an href="project/_id" attribute must exist for SEO and for WCAG compliance.

Attempts

I tried to redefine renderListProjects as

  renderListProjects() {
    var pj = this
    return this.data.projects.map(function(projectform,i) {
      return <li key={"li"+i}><a onClick={pj.click(projectform._id)} href={Meteor.absoluteUrl()+'project/' + projectform.username +'/' +projectform._id} key={"a"+i}>Project {projectform._id}</a></li>;
    });
  },
click(id) {
    var currentApp = ProjectForm.findOne({_id:id}, {sort: {createdAt: -1}});
    this.setState({input36:input36});
  },

But when I run my meteor+react project, my browser crashes because some kind of infinite loop is happening.

2

Answers


  1. Chosen as BEST ANSWER

    This code solved the problem

    getMeteorData() {
        var myForm = ProjectForm.findOne({_id:this.props.router.params._id});
        var projects = ProjectForm.find({});
        return {myForm:myForm,projects:projects};
        // doing a console.log(myForm); would give you something like
        /*
        input1:my text 1
        input2:some other text
        input3:something else
        etc....
        */
      },
      clickLoadForm(appId)
      {
        var currentApp = Projects.findOne({appId}, {sort: {createdAt: -1}});
        var state = new Object();
        var refs = this.refs;
        Object.keys(refs).map(function(prop,index){
          state[prop] = typeof currentApp[prop] == 'undefined' ? "" : currentApp[prop];
        });
        this.setState(state);
      },
      handleChange: function(e) {
        if(!e.target.id) return;
        if(typeof e.target.id == 'undefined') return;
        var state = new Object();
        state[e.target.id] = e.target.value;
    
        this.setState(state);
      },
      renderListProjects() {
    
        return this.data.projects.map(function(projectform,i) {
          return <li key={"li"+i}><a onClick={_this.clickLoadForm.bind(_this,projectform._id)} href={Meteor.absoluteUrl()+'project/' +projectform._id} key={"a"+i}>Project {projectform._id}</a></li>;
        });
      },
      getInitialState() {
         return Projects.findOne({this.props.router.params._id}, {sort: {createdAt: -1}});
      },
      render() {
        return (
          <div>
          <ul>{this.renderListProjects()}</ul>
          <form>
             <span>Hello this is some text</span>
             <input type="text" ref="input1" id="input1" onChange={this.handleChange} />
             <p>Blah blah this is boring</p>
             <input type="text" ref="input2" id="input2" onChange={this.handleChange} />
             <img src="image-of-a-kangaroo.png" />
             <input type="text" ref="input3" id="input3" onChange={this.handleChange} />
             <ul>
                <li>Buy brocolli</li>
                <li>Buy oregano</li>
                <li>Buy milk</li>
             </ul>
             <input type="text" ref="input4" id="input4" onChange={this.handleChange} />
             ...
             <textarea ref="input100" id="input100" onChange={this.handleChange}/>
             <input type="text" ref="input101" id="input101" onChange={this.handleChange} />
             <p><strong>Yes, I like pizza!</strong>  But my porcupine gets sick eating pizza.</p>
             ...
          </form>
          </div>
        );
    

    To summarize, my changes were:

    1. created a renderListProjects() function
    2. created the clickLoadForm() function and used it in the renderListProjects()
    3. created the handleChange() function and made sure it's references via onChange for each element in render()
    4. made sure every element in render() has an id that's the same as the ref

  2. What you created is what React calls an controlled input. This means that React has declared your value in the inputs to correspond to whatever this.data.myForm.input1 points to.

    What you need to do in order to be able to change an input field is to add an onChange handler that takes care of updating the value. The documentation is quite straight forward but I added a small example below to illustrate it.

    class Form extends React.Component {
      constructor() {
        super();
        this.state = {
          myForm: {
            inputs: [
              {value: 1}, {value: 2}, {value: 3}
            ]
          }
        };
      }
      onChange = (index, event) => {
        let inputs = this.state.myForm.inputs.slice(0);
        inputs[index] = {
          value: parseInt(event.target.value, 10)
        };
    
        this.setState({
          myForm: {
            inputs: inputs
          }
        })
      }
      render() {
        return (<form>
          {this.state.myForm.inputs.map((input, index) => {
            return (<input onChange={this.onChange.bind(null, index)} value={input.value} key={index} />);
          }, this)}
          </form>);
      }
    };
    
    ReactDOM.render(<Form />, document.querySelector('#c'));
    
    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search