0

Once I started passing props from parent to child to child I have been getting this problem where the getQuestion function only gets me the first letter typed, In addition in the input field nothing shows up.

Before when my code was just Parent to child it worked.

I want to know what exactly is going on because I have tried debugging by console logging and all I know is certain is that it only registers the first letter.

This question did not help because I have not misspelled onChange.

Can't type in React input text field

App.js

class App extends Component {
  constructor(props){
    super(props);



    this.getPostId = this.getPostId.bind(this);
    this.getQuestion = this.getQuestion.bind(this);
    this.makePost = this.makePost.bind(this);
    this.getBody = this.getBody.bind(this);
    this.getPostType = this.getPostType.bind(this);
    this.getImgSrc = this.getImgSrc.bind(this);
    this.submitPost = this.submitPost.bind(this);
    this.formOpen = this.formOpen.bind(this);
    this.formClose = this.formClose.bind(this);

    this.back = this.back.bind(this);
    this.showPost = this.showPost.bind(this);
    this.renderPosts = this.renderPosts.bind(this);

    //Answer/Response methods
    this.makeAnswer = this.makeAnswer.bind(this);
    this.getAnswer = this.getAnswer.bind(this);
    this.submitAnswer = this.submitAnswer.bind(this);

    this.state = {
      posts: [],

        answers: [],

      question: '',
      body: '',
      postType: 'Question',
      imgSrc: '',
      form: false,
      openedPost: null,
      answer: '',
      favorited: false,

      //sign up
      email:'',
      password: '',
      user: null

    }

}
getQuestion(event) {
  event.preventDefault();
  this.setState({ question:event.target.value });
}


render() {


    return (

      <Router>
        <div className="container">
          <Route
                  exact path={"/"}
                  component={() => <Home />}
          />

          <Route
                  exact path={"/home"}
                  component={() => <Home />}
          />

          <Route
                exact path={"/signup"}
                component={() => <SignUp />}
          />

          <Route
                exact path={`/dashboard`}
                component={() =>
                  <Dashboard back={this.back}
                    form={this.state.form}
                      openedPost={this.state.openedPost}
                        renderPosts={this.renderPosts}
                          formClose={this.formClose}
                            formOpen={this.formOpen}
                              posts={this.state.posts}
                                getPostId={this.getPostId}
                                  getQuestion={this.getQuestion}
                                    makePost={this.makePost}
                                      getBody={this.getBody}
                                        getPostType={this.getPostType}
                                          getImgSrc={this.getImgSrc}
                                              submitPost={this.submitPost}
                                                test={this.test}
                                                  question={this.state.question}

                />}
              />



                <Route
                      exact path={`/dashboard/post${this.state.openedPost}`}
                      component={() =>
                        <SinglePost posts={this.state.posts}
                                      openedPost={this.state.openedPost}
                                       getAnswer={this.getAnswer}
                                        makeAnswer={this.makeAnswer}
                                          submitAnswer={this.submitAnswer}
                                            showAnswers={this.showAnswers}
                                              renderAnswers={this.renderAnswers}
                                                renderFavorite={this.renderFavorite}
                                                  userFavorited={this.userFavorited}
                                                    back={this.back}

                      />
                    }

                    />






      </div>
    </Router>
    );
  }

Dashboard.js

import React, { Component } from 'react';
import Navagationbar from  '../../components/Navigation/Navagationbar';
import Header from  '../../components/Header/Header';
import SignUpButton from  '../../components/SignUp/SignUpButton';
import AddPostForm from './AddPostForm';
import './styles.css';

import {
  Link
} from 'react-router-dom'

class Dashboard extends Component {
  render() {


    let renderedPosts = null;



    let createPostButton =  <div className="container" ><button className="button-primary" onClick={this.props.formOpen}> Create Post </button> </div>;

    if(this.props.openedPost) {
      renderedPosts = null;
      createPostButton = null;
    }
    else {
      renderedPosts = this.props.renderPosts();
    }

        let createPostForm = null;
        const openedForm = this.props.form;
        if(openedForm) {
        createPostForm =
              <AddPostForm
                formClose={this.props.formClose}
                    posts={this.props.posts}
                      getPostId={this.props.getPostId}
                        getQuestion={this.props.getQuestion}
                          makePost={this.props.makePost}
                            getBody={this.props.getBody}
                              getPostType={this.props.getPostType}
                                getImgSrc={this.props.getImgSrc}
                                    submitPost={this.props.submitPost}
                                      question={this.props.question}

                                    />

          createPostButton = null;
        }
        console.log("OPENED FORM IS " + openedForm)
    return (
    <div >
      <SignUpButton />
      <Header />
      <button onClick={this.props.test}/>
      {this.props.openedPost ? null : <Navagationbar />}



        {createPostForm}
        <div className="row">
          <div>
            {createPostButton}
          </div>
        </div>

        <div className="row">



        </div>
      <div className="row">
        <div className="twelve columns">
            {renderedPosts}
        </div>
      </div>

    </div>
    );
  }
}


export default Dashboard;

AddPostForm.js

import React, { Component } from 'react';
import './styles.css';

class AddPostForm extends Component {
    render() {
    return(
      <div className="container">



      <div className="row">
            <div className="six columns">
              <label>Post Title</label>
                <input onChange={this.props.getQuestion} value={this.props.question} className="u-full-width" type="search" placeholder="title" id="exampleEmailInput"/>
            </div>

              <div className="six columns">
                <label>Post Type</label>
                  <select value={this.props.type} onChange={this.props.getPostType} className="u-full-width">
                    <option value="Question">Question</option>
                    <option value="Discussion">Discussion</option>

                 </select>
              </div>
        </div>
            <div className="row">
              <div className="twelve columns">
                <label>Post</label>
                  <textarea onChange={this.props.getBody}  className="u-full-width" placeholder="get some clout" id="postMessage"></textarea>
                <label>
                  <span>Image Link</span> <br />
                    <input type="search"  onChange={this.props.getImgSrc}/>
                </label>
                  <input className="button-primary" type="button" value="submit" onClick={this.props.submitPost}/>
                  <button onClick={this.props.formClose}>Cancel </button>
               </div>
            </div>

    </div>

    );
  }
}

export default AddPostForm;

edit: After removing event.preventDefault() from getQuestion I can type but why does the input field unfocus after typing a single letter.

Is it because after every time I type the input field re-renders?

edit: Added majority of the code as requested.

This is sufficient in my opinion let me know if you want the rest of the functions.

codejockie
  • 9,020
  • 4
  • 40
  • 46
Omar
  • 3,401
  • 2
  • 23
  • 40
  • You're doing `event.preventDefault()`??! – Andrew Li Dec 17 '17 at 17:15
  • okay, thats weird earlier that did not prevent me. That however does not fix it completely. I think I need to focus the input because after typing a letter the user has to click on the input box again to type. Do you know why after typing one letter it unfocuses? @Li357 – Omar Dec 17 '17 at 17:18
  • Okay, theres a few questions I have seen on the react docs that i should maybe do ` ` question is just the string that I store the input in state in App.js. When I do that and take away event.prevent.default() I can type but the text unfocuses after every letter and the user has to click on it again. Now when I just remove event.preventDefault() with the code I have provided above does not allow me to type. @Li357 – Omar Dec 17 '17 at 17:24
  • Please could you show full code to each file, I might be able to help. – codejockie Dec 17 '17 at 17:36
  • @JohnKennedy added a majority of the code let me know if it is sufficient – Omar Dec 17 '17 at 17:48
  • Could you use codesanbox.io to create the project with all files included then paste the link here so I can edit as required. This is because, trying to reproduce this issue require that I have access to the component files imported in `Dashboard.js` like `Header`, `Navigation` and `SignUp` – codejockie Dec 17 '17 at 17:53
  • Do you NEED to be tracking constant change from the highest level component? It would be better to track constant change from the component they are typing in, then onSubmit, send that lowest level state through to the parent and THEN setState of the entire entry the user typed. – Gavin Thomas Dec 17 '17 at 18:08
  • recreated it as best as I could just navigate to dashboard https://codesandbox.io/s/xpp107x1wq @JohnKennedy – Omar Dec 17 '17 at 18:15
  • Hi @Omar, I fixed the issue please check my updated answer. – codejockie Dec 18 '17 at 06:12

3 Answers3

3

Having gone through the code, I noticed all methods come from the root level component App. In which case when you type in the post title input field it immediately calls the parent getQuestion method which set state there by causing a re-render of the page which in turn causing the input field to lose focus.

Method 1:
To fix this I'd suggest you maintain state for the AddPostForm by allowing it manage its own state.

import React, { Component } from 'react';

class AddPostForm extends Component {
  state = {
    question: ""
  }

  setQuestion = (event) => {
    this.setState({
      question: event.target.value
    });
  }

  render() {
    return (
      <div className="container">
        <div className="row">
          <div className="six columns">
            <label>Post Title</label>
            <input
             onChange={this.setQuestion} // note change
             value={this.state.question} // note change
             className="u-full-width"
             type="search"
             placeholder="title"
             id="exampleEmailInput"
            />
          </div>
          ...
        </div>
      </div>
    );
  }
}

export default AddPostForm;

Method 2:
In App.js render method I made few changes that will allow you pass down props to the child components without the text fields loosing focus.

render() {
    return (

      <Router>
        <div className="container">
          <Route
            exact
            path="/"
            component={Home}
          />

          <Route
            exact
            path="/home"
            component={Home}
          />

          <Route
            exact
            path="/signup"
            component={SignUp}
          />

          <Route
            exact
            path="/dashboard"
            render={(props) =>
              <Dashboard
                {...props}
                back={this.back}
                body={this.state.body}
                form={this.state.form}
                openedPost={this.state.openedPost}
                renderPosts={this.renderPosts}
                formClose={this.formClose}
                formOpen={this.formOpen}
                posts={this.state.posts}
                getPostId={this.getPostId}
                getQuestion={this.getQuestion}
                makePost={this.makePost}
                getBody={this.getBody}
                getPostType={this.getPostType}
                getImgSrc={this.getImgSrc}
                submitPost={this.submitPost}
                test={this.test}
                question={this.state.question}
              />
            }
          />

          <Route
            exact
            path={`/dashboard/post${this.state.openedPost}`}
            render={(props) =>
              <SinglePost
                {...props}
                posts={this.state.posts}
                openedPost={this.state.openedPost}
                getAnswer={this.getAnswer}
                makeAnswer={this.makeAnswer}
                submitAnswer={this.submitAnswer}
                showAnswers={this.showAnswers}
                renderAnswers={this.renderAnswers}
                renderFavorite={this.renderFavorite}
                userFavorited={this.userFavorited}
                back={this.back}
              />
            }
          />
        </div>
      </Router>
    );
  }

In the Routes for dashboard I changed from using component prop of Route to using render prop instead. This fixes the issue.

Edit Q 47857536 SO

codejockie
  • 9,020
  • 4
  • 40
  • 46
1

To improve it you can clear the form after the state data is send to the parent component.

handleChange(e) {
 let { name, value } = e.target;

 // clone current state
 let clonedState = Object.assign({}, this.state);

 clonedState.data[name] = value;

 this.setState({
   data: clonedState.data,
 });
}

handleSubmit(e) {
  e.preventDefault();
  this.props.getQuestion(this.state.data)

  // clear state data
  this.setState({ data: {} });
}
Dave Bitter
  • 101
  • 9
0

It would be better to track changes in the component the user is actually typing in. Then onSubmit, call your getQuestion(this.state).

This would be in any form component a user is typing in.

    handleChange(e) {
    let { name, value } = e.target;
    this.setState({
      [name]: value,
    });
  }

  handleSubmit(e) {
    e.preventDefault();
    this.props.getQuestion(this.state)
  }
Gavin Thomas
  • 1,827
  • 2
  • 11
  • 17