5
\$\begingroup\$

I am building a movie search React app using themoviedb.org API. To make ajax call to pull the list of movies I need to get input value as a variable and feed to the url, but not sure how to fetch a value that belongs to another component.

I've made an extensive online search, but they mainly refer to the case when it happens inside same component, and using ref is discouraged.

So what would be the best way to organize an app in React way, keeping components uncoupled yet able to feed input value to url of another component?

   import React from 'react';
   import './App.css';
   import axios from 'axios';


class SearchForm extends React.Component {
  constructor(props) {
    super(props);
    this.state = {value: ''};

    this.handleChange = this.handleChange.bind(this);
    this.handleSubmit = this.handleSubmit.bind(this);
  }


  handleChange(event) {
    this.setState({value: event.target.value});
  }

  handleSubmit(event) {
    console.log("state value is " + this.state.value);

    var searchValue = this.movieName.value;
    console.log("ref value is "+ searchValue)

    event.preventDefault();
  }


  render() {

    return (
      <form onSubmit={this.handleSubmit}>
        <label>
          Name:
          <input className="movieName" type="text" ref={(input) => { this.movieName = input; }} value={this.state.value} onChange={this.handleChange} />

        </label>
        <input type="submit" value="Submit" />
        <h1>{this.state.value}</h1>
      </form>
    );
  }
}



class App extends NameForm{  /* I am extending NameForm to get access to input value, but probably coupling components too tight */
    constructor(props) {
      super(props);
      this.state ={
        movie:[]
      };
    }



componentDidMount() { 


  let searchInput = "land"; /* This should be from SearchForm's value */


let sortByPop = "&sort_by=popularity.desc";
let requestUrl = 'https://api.themoviedb.org/3/search/movie?api_key=f8c4016803faf5e7f424abe98a04b8d9&query=' + searchInput + sortByPop;

  axios.get(requestUrl).then(response => {
      this.setState({movie: response.data.results})
  });



}

render() {

 let baseImgURL = "https://image.tmdb.org/t/p/w185_and_h278_bestv2";
 let posterImgPath = this.state.movie.map(movie => movie.poster_path);


 let posterLink = baseImgURL + posterImgPath;

  return(

      <div className="App">
        <Header />    
       <SearchForm />   

   <div> 
          {this.state.movie.map(movie =>
           <div className="movieTitle">
           <div className="movieCard">
           <img className="posterImg" src= {`https://image.tmdb.org/t/p/w185_and_h278_bestv2/${movie.poster_path}`} alt={movie.title} />
           <div className="searchFilmTitles" key={movie.id}>{movie.title}</div>
           </div>
           </div>
           )} 
      </div>

   </div>
  )
}

}


export default App;
\$\endgroup\$
1
  • 1
    \$\begingroup\$ Please do not update the code in your question to incorporate feedback from answers, doing so goes against the Question + Answer style of Code Review. This is not a forum where you should keep the most updated version in your question. Please see what you may and may not do after receiving answers. \$\endgroup\$
    – Zeta
    Commented May 11, 2017 at 17:17

1 Answer 1

1
\$\begingroup\$

So the way you would accomplish this in my experience is to move your state up your component tree to a higher level where each component that requires access to that state can have it via props that you pass in.

So for example your SearchForm component could accept all the values and functions it requires via props. It needs a prop to handle the change of the search input as the user types, a prop to handle the submit as the user clicks the submit button and a prop to show the search input value in the input box.

So, for example:

<SearchForm
    searchInput={this.state.searchInput}
    handleChange={(event) => this.setState({ searchInput: event.target.value })}
    handleSubmit={() => this.searchForMovie(this.state.searchInput)}
/>

That means your SearchForm could become something more like:

const SearchForm = (props) => (
  <form onSubmit={props.handleSubmit}>
    <label>
      Name:
      <input 
        className="movieName" 
        type="text" 
        value={props.searchInput} 
        onChange={props.handleChange}
      />
    </label>
    <input type="submit" value="Submit" />
    <h1>{props.searchInput}</h1>
  </form>
);

All your state lives in your App component and now you can use that state to pass on as props to any other child components that may need it.

Some observations/suggestions:

  • As you can see above there is a searchForMovie() function, I would extract the logic from your componentDidMount hook to a function like this so you can call it whenever needed with the search input you desire
  • Also consider some error handling on your axios.get() function call as if anything goes wrong with the request you won't know about it because the promise will swallow that (can be confusing when debugging)
  • The above does not consider any performance optimisations and the amount of renders/rerenders on a large app may be problematic, but my guess is that would be premature for your app and important is first that you get it to work
  • For your App class I would advise against extending any other class aside from React.Component as that is all you require (if I understand your question correctly)
  • Also, if you are using this in your class methods you will need to remember to bind them in your constructor to avoid hitting the unknown function error, i.e. this.searchForMovie = this.searchForMovie.bind(this);
\$\endgroup\$
2
  • \$\begingroup\$ Thanks for the great observations/suggestions. I've made corrections (as seen in edit 1 above), and input value is still not passing properly to App component, unable to update input value. Am I missing anything? \$\endgroup\$
    – amma
    Commented May 11, 2017 at 17:16
  • \$\begingroup\$ @amma Maybe put together a CodePen so I can see where you are at \$\endgroup\$
    – Rory
    Commented May 12, 2017 at 1:30

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.