2

I'm implementing movie search functionality using the moviedb api. I have implemented in React only but I want to do it in Redux. Here is my approach in React.

Header.js

import React, { Component } from "react"
import { Navbar, Form, FormControl } from "react-bootstrap"
import { NavLink } from "react-router-dom"
import axios from "axios"
import MovieCards from "./MovieCards"
const apiKey = process.env.REACT_APP_MOVIE_DB_API_KEY

class Header extends Component {
  state = {
    isSearching: false,
    value: "",
    movies: []
  }

  searchMovies = async val => {
    this.setState({ isSearching: true })
    const res = await axios.get(
      `https://api.themoviedb.org/3/search/movie?api_key=${apiKey}&language=en-US&query=${val}&page=1&include_adult=true`
    )
    const movies = await res.data.results
    this.setState({ movies: movies, isSearching: false })
  }

  handleChange = e => {
    const { name, value } = e.target
    this.searchMovies(value)
    this.setState({
      [name]: value
    })
  }

  render() {
    return this.state.value === "" ? (
      <div>
        <Navbar
          bg="dark"
          expand="lg"
          style={{ justifyContent: "space-around" }}
        >
          <NavLink to="/">
            <Navbar.Brand>Movie Catalogue</Navbar.Brand>
          </NavLink>
          <Navbar.Toggle aria-controls="basic-navbar-nav" />
          <Navbar.Collapse id="basic-navbar-nav">
            <Form inline>
              <FormControl
                type="text"
                placeholder="Search"
                className="mr-sm-2"
                onChange={this.handleChange}
                name="value"
                value={this.state.value}
              />
            </Form>
          </Navbar.Collapse>

          <NavLink to="/popular">Popular</NavLink>
          <NavLink to="/now-playing">Now Playing</NavLink>
          <NavLink to="/top-rated">Top Rated</NavLink>
          <NavLink to="/upcoming">Upcoming</NavLink>
        </Navbar>

        {this.state.movies.map((movie, i) => {
          return <MovieCards key={i} movie={movie} />
        })}
      </div>
    ) : (
      <div>
        <Navbar
          bg="dark"
          expand="lg"
          style={{ justifyContent: "space-around" }}
        >
          <NavLink to="/">
            <Navbar.Brand>Movie Catalogue</Navbar.Brand>
          </NavLink>
          <Navbar.Toggle aria-controls="basic-navbar-nav" />
          <Navbar.Collapse id="basic-navbar-nav">
            <Form inline>
              <FormControl
                type="text"
                placeholder="Search"
                className="mr-sm-2"
                onChange={this.handleChange}
                name="value"
                value={this.state.value}
              />
            </Form>
          </Navbar.Collapse>

          <p style={{ color: "white" }}>
            Search results for " {this.state.value} "
          </p>
        </Navbar>

        {this.state.movies.map((movie, i) => {
          return <MovieCards key={i} movie={movie} />
        })}
      </div>
    )
  }
}

export default Header

I want to do it using Redux, so I'm doing it this way.

Header.js

import React, { Component } from "react"
import { Navbar, Form, FormControl } from "react-bootstrap"
import { NavLink } from "react-router-dom"
import axios from "axios"
import { connect } from "react-redux"
import { movieSearch } from "../actions/index"
import MovieCards from "./MovieCards"
const apiKey = process.env.REACT_APP_MOVIE_DB_API_KEY

class Header extends Component {

  handleChange = e => {
    const { name, value } = e.target
    this.props.dispatch(movieSearch(value)) // I'm not sure if this is the right approach. I'm dispatching and then setting state.
    this.setState({
      [name]: value
    })
  }

  render() {
    return this.state.value === "" ? (
      <div>
        <Navbar
          bg="dark"
          expand="lg"
          style={{ justifyContent: "space-around" }}
        >
          <NavLink to="/">
            <Navbar.Brand>Movie Catalogue</Navbar.Brand>
          </NavLink>
          <Navbar.Toggle aria-controls="basic-navbar-nav" />
          <Navbar.Collapse id="basic-navbar-nav">
            <Form inline>
              <FormControl
                type="text"
                placeholder="Search"
                className="mr-sm-2"
                onChange={this.handleChange} 
                name="value" 
                value={this.state.value} 
              />
            </Form>
          </Navbar.Collapse>

          <NavLink to="/popular">Popular</NavLink>
          <NavLink to="/now-playing">Now Playing</NavLink>
          <NavLink to="/top-rated">Top Rated</NavLink>
          <NavLink to="/upcoming">Upcoming</NavLink>
        </Navbar>

        {this.state.movies.map((movie, i) => {
          return <MovieCards key={i} movie={movie} />
        })}
      </div>
    ) : (
      <div>
        <Navbar
          bg="dark"
          expand="lg"
          style={{ justifyContent: "space-around" }}
        >
          <NavLink to="/">
            <Navbar.Brand>Movie Catalogue</Navbar.Brand>
          </NavLink>
          <Navbar.Toggle aria-controls="basic-navbar-nav" />
          <Navbar.Collapse id="basic-navbar-nav">
            <Form inline>
              <FormControl
                type="text"
                placeholder="Search"
                className="mr-sm-2"
                onChange={this.handleChange}
                name="value"
                value={this.state.value}
              />
            </Form>
          </Navbar.Collapse>

          <p style={{ color: "white" }}>
            Search results for " {this.state.value} "
          </p>
        </Navbar>

        {this.state.movies.map((movie, i) => {
          return <MovieCards key={i} movie={movie} />
        })}
      </div>
    )
  }
}

const mapStateToProps = (state) => {
   return state
}

export default connect(mapStateToProps)(Header)

actions/index.js

export const movieSearch = val => {
  const movieSearchUrl = `https://api.themoviedb.org/3/search/movie?api_key=${apiKey}&language=en-US&query=${val}&page=1&include_adult=true`

  return async dispatch => {
    dispatch({ type: "SEARCHING_MOVIES_START" })
    try {
      const res = await axios.get(movieSearchUrl)
      dispatch({
        type: "SEARCHING_MOVIES_SUCCESS",
        data: { searchResults: res.data.results }
      })
    } catch (err) {
      dispatch({
        type: "SEARCHING_MOVIES_FAILURE",
        data: { error: "Could not find the movie" }
      })
    }
  }
}

reducers/movieSearchReducer.js

const initialState = {
  value: "",
  isSearchingMovies: false,
  isSearchedMovies: false,
  movieList: [],
  searchingError: null
}

export const movieSearchReducer = (state = initialState, action) => {
  switch (action.type) {
    case "SEARCHING_MOVIES_START":
      return {
        ...state,
        isSearchingMovies: true,
        searchingError: null
      }
    case "SEARCHING_MOVIES_SUCCESS":
      return {
        ...state,
        isSearchingMovies: false,
        isSearchedMovies: true,
        movieList: action.data,
        searchingError: null
      }
    case "SEARCHING_MOVIES_FAILURE":
      return {
        ...state,
        searchingError: action.data.error
      }
  }
}

I'm not sure how to implement the part of the below input form part in Redux. Please help if you can.

    onChange={this.handleChange}
    name="value"
    value={this.state.value}
2
  • I'm not sure what part of your store you want to be connected to your component. But what you need is the connect function from react-redux. Commented Mar 6, 2020 at 10:45
  • Sorry forgot to write that part. Updated! Commented Mar 6, 2020 at 10:59

2 Answers 2

1

When you change from state in component to redux, you will generally remove the react state and pickup the redux state from the 'props'.

So step 1 is to get rid of your setState all together.

value={this.state.value}

will become

value={this.props.movieList}

In order to get the movieList in the props, you need to wrap your component in a 'container' and use mapStateToProps to map the redux state to your props.

See https://react-redux.js.org/api/connect for more details

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

5 Comments

How would I do update the state variable in Redux like that I have done in React. ``` this.setState({ [name]: value }) ``` And what about this.handleChange?
I think you might be wrong here. this.state.value is the value that the user has typed. How would it be this.props.movieList?
oops sorry, yeah you cant keep that state, I meant this.state.movies should come from redux, so say this.props.movies (assuming that is how you map it). Have you implemented the 'connect' and mapStateToProps ?
Yeah I've implemented it. Then how would I update the state on every keystroke? Basically just have to update the value of value variable.
by the way you can move that value to redux as well rather than the state, the only thing is i do not see the point, and it does perform a bit slow, but what you do want to store in redux is the final value, and then you need to populate the state when ever component mounts...
0

If you use Redux to store the movies, you can delete the local state to your component, and use redux's movieList prop instead.

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.