1

I am creating a React Application for searching for movies using the OMDb.

My app works in two stages:

  • You are presented initially with a search box
  • Upon submitting the search box with a movie query, the app should redirect to a results page.

Below is my Main application:

class Muuvies extends Component {
    constructor() {
        super();

        this.state ={
            movies: [],
            error: "",
            searchedFor: "",
        };

        this.search = this.search.bind(this);
    }

    search({data}) {
        fetch(`http://www.omdbapi.com/?apikey=${OMDB_API_KEY}&s=${data}&type=movie`)
        .then(res => res.json())
        .then(res => {
            return res;
        }).then(json => this.setState({
            searchedFor: data,
            movies: propOr([], 'Search', json),
            error: json.Error
        })).catch(err => this.setState({
            error: 'Error Occurred: Try Again',
            movies: [],
            searchedFor: ''
        }));

        // if there wasn't an error, redirect to the Results page, passing what is returned from fetch().
    }

    render() {
    return (
        <Container>
            <Logo role="img" aria-label="search">🍿</Logo>
            <SearchWrapper>
                <Search search={this.search} />
                {
                    this.state.error
                    ? <Error><span role="img" aria-label="error" style={{marginRight: 2 + 'px'}}>⚠️</span>{this.state.error}</Error>
                    : null
                }
            </SearchWrapper>
        </Container>
    );
    }
}

How do I change my application so, that if the user submits the form and there isn't an error (e.g. this.state.error is empty after the fetch()), then it redirects them to the Results component?

Contents of Search.js:

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

    this.state = {
      data: '',
    };

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

  handleSubmit(e) {
    e.preventDefault();
    this.props.search(this.state);
    this.setState({ data: '' });
  }

  render () {
    return (
      <StyledForm onSubmit={this.handleSubmit}>
        <StyledSearch id="data" type="text"
            name="data"
            placeholder="Search for Muuvies."
            value={path(['state', 'data'], this)}
            onChange={this.handleChange}
            autoComplete="off"
            autoFocus={true}
        />
      </StyledForm>
    )
  }
}
5
  • history has a method push() than you can use to programatically navigate. history should be available on the component's props.You'd need to use withRouter to access this. Commented Sep 27, 2018 at 19:40
  • @AlexanderStaroselsky So, something like if (!this.state.error) this.props.history.push(Results)? Commented Sep 27, 2018 at 19:42
  • I'll create an answer to demonstrate, but can I ask why you'd have an error inside your then()? Commented Sep 27, 2018 at 19:42
  • I think you should ask yourself why you want results to be its own separate route. What is the point of that? Typically, you would not want a route in your application to depend upon the prior state of your app. What happens if someone goes directly to the results route of your app without having done a search? If you can't send someone a URL to a results route and have them do anything useful there, what is the point? Instead, maybe have just the search route and render the results there as needed. Commented Sep 27, 2018 at 19:50
  • For every route in your app, ask yourself "would my user be able to bookmark this in their browser? Will they be able to return to it in a later session, considering that React state will be lost? What will they see when they return to the bookmark and how will it be useful to them?" Commented Sep 27, 2018 at 19:52

2 Answers 2

2

Given you are using react-router-dom you can use history's push() method. This would need to be used with wrapping component withRouter to expose the necessary props. This would also need to be done with then() do to the async nature of your API call/search:

import { withRouter } from 'react-router-dom';

class Muuvies extends Component {
    constructor() {
        super();

        this.state ={
            movies: [],
            error: "",
            searchedFor: "",
        };

        this.search = this.search.bind(this);
    }

    search({data}) {
        fetch(`http://www.omdbapi.com/?apikey=${OMDB_API_KEY}&s=${data}&type=movie`)
        .then(res => res.json())
        .then(res => {
            return res;
        }).then(json => {
          // you may not need this if your are redirecting
          this.setState({
              searchedFor: data,
              movies: propOr([], 'Search', json),
              error: json.Error
          }, () => {
            this.props.history.push("/results"); // or whatever string path
          });
        }).catch(err => this.setState({
            error: 'Error Occurred: Try Again',
            movies: [],
            searchedFor: ''
        }));

        // this would execute before API call/search is complete, need to do it in then()
    }

    render() {
    return (
        <Container>
            <Logo role="img" aria-label="search">🍿</Logo>
            <SearchWrapper>
                <Search search={this.search} />
                {
                    this.state.error
                    ? <Error><span role="img" aria-label="error" style={{marginRight: 2 + 'px'}}>⚠️</span>{this.state.error}</Error>
                    : null
                }
            </SearchWrapper>
        </Container>
    );
    }
} 

export default withRouter(Muuvies);

This is assuming you have a Route defined for your Results component/path:

import Results from './path/to/Results';

// ...

<Route path="/results" component={Results} />

You could also use the Redirect component in render() method, perhaps based on some state values:

import { Redirect } from 'react-router-dom';

// ...

render() {
   // check for error and maybe some other flag(s) such as a boolean property
   if (!this.state.error && this.state.submitted && this.state.movies.length > 0) {
     return <Redirect to={{ pathname: '/results', state: { movies: this.state.movies } }} />
   }

   // else render form or error maybe
}

That being demonstrated, if you are planning to navigate at the component's level to "/results" or whatever the path may be, You probably wouldn't need to set state. You can pass state as a second argument to the history.push(path, state) method if necessary.

Hopefully that helps!

Sign up to request clarification or add additional context in comments.

2 Comments

This is great thanks, but how do I get react-router to route to the Results.js file?
Well, you have something like <Route path="/results" component={Results} /> defined in your application right? You may need to share your <Route /> configuration.
0

You can write your code here:

search({data}) {
    fetch(`http://www.omdbapi.com/?apikey=${OMDB_API_KEY}&s=${data}&type=movie`)
    .then(res => res.json())
    .then(res => {
        return res;
    }).then(json => {
        this.setState({
           searchedFor: data,
           movies: propOr([], 'Search', json),
           error: json.Error
        });
        //------
        //Write your code here, because if you got there, then there was no error at none of the "then" above
        //Or if a success response can contain an error field, you can check it with an if statement
        //------
    }).catch(err => this.setState({
        error: 'Error Occurred: Try Again',
        movies: [],
        searchedFor: ''
    }));

The code depends of how are you structurating your project and which libraries are you using.

Comments

Your Answer

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

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.