frre tyy
frre tyy

Reputation: 363

React Component not reflecting current state of store

I have implemented a component that shows courses. Within the same component, there is functionality for filtering courses when a user searches for a certain course.

The problem I am having is when I dispatch an action for filtering courses, the redux store shows the courses state has been updated to contain only courses that match the search query but the component does not reflect the new state(i.e still shows all courses). How can I resolve this issue?

Here is what I have implemented so far

Action

export const retrieveAllCourses = (url, query) => dispatch => {
  return axios
    .get(url + `?title=${query}`, {
      headers: myHeaders
    })

    .then(res => {
      const fetchall = {
        type: ACTION_TYPE.VIEW_COURSES,
        payload: res.data.courses
      };
      dispatch(fetchall);
    })
    .catch(error => {
      toast.error(error, { autoClose: 3500, hideProgressBar: true });
    });
};

Reducer

import ACTION_TYPE from "../../actions/actionTypes";

const initialState = {
  courses: [],
};

const coursesReducer = (state = initialState, action) => {
  switch (action.type) {

  case ACTION_TYPE.VIEW_COURSES:
    return {
      ...state,
      courses: state.courses.concat(action.payload.results),
    };

  default:
    return state;
  }
};

export default coursesReducer;

Search component

import React from "react";
import PropTypes from "prop-types";

class Search extends React.Component {
  state = {
    search: ""
  };

  handleChange = ev => {
    ev.preventDefault();
    const { onChange } = this.props;
    const search = ev.target.value;
    this.setState({ search });
    if (onChange) {
      onChange(search);
    }
  };

  handleSearch = ev => {
    ev.preventDefault();
    const { onSearch } = this.props;
    const { search } = this.state;
    if (onSearch) {
      onSearch(search);
    }
  };

  render() {
    const { id } = this.props;

    window.onload = function() {
      var input = document.getElementById("search-input");
      input.addEventListener("keyup", function(event) {
        if (event.keyCode === 13) {
          event.preventDefault();
          document.getElementById("mySearchBtn").click();
        }
      });

    };

    return (
      <React.Fragment>
        <form id={id}>
          <div className="search-container">
            <div className="input-group">
              <input
                type="text"
                id="search-input"
                className="search-input form-control"
                placeholder="Search lessons"
                onChange={this.handleChange}
              />
            </div>
            <div>
              <button id="mySearchBtn" onClick={this.handleSearch} />
            </div>
          </div>
        </form>
      </React.Fragment>
    );
  }
}

Search.propTypes = {
  onChange: PropTypes.func,
  onSearch: PropTypes.func,
  id: PropTypes.number
};

export default Search;

Navbar component

import React from "react";
import PropTypes from "prop-types";
import Search from "../../components/courses/Search";

export const LoggedInNavBar = ({ handleSearch, handleSearchChange}) => {
  return (
    <div className="row bobo-menu">
      <div className="col-sm">
        <div className="logo-container">
          <a href={"/courses"}>
            <h1 id="logo" className="hidden">
              TUTORIALS
            </h1>
          </a>
        </div>
      </div>

      <div className="col-sm">
        <Search onSearch={handleSearch} onChange={handleSearchChange} />
      </div>

      <div className="col-sm">
        <p id="motto">
          TUTORIALS<span className="text-succes"> AND</span> LEARNING{" "}
          <span className="text-succes">FOR ALL</span>
        </p>
      </div>

      <div className="col-sm">
        <div className="row row__menu__icons">
          <div className="col-lg-3 col-md-3 col-sm-3">
            <a
              id="title"
              href="/create"
              className="btn btn-login "
              data-mode="signin"
            >
              {" "}
              <i className="fas fa-plus" />
            </a>
          </div>
          <div className="col-lg-3 col-md-3 col-sm-3">
            <a
              id="title"
              href="/create"
              className="btn btn-login "
              data-mode="signin"
            >
              <i className="far fa-user" />
            </a>
          </div>
          <div className="col-lg-3 col-md-3 col-sm-3">
            <a
              id="title"
              href="/me/stories"
              className="btn btn-login "
              data-mode="signin"
            >
              <i className="fas fa-book" />
            </a>
          </div>
          <div className="col-lg-3 col-md-3 col-sm-3">
            <a
              id="title"
              href="/create"
              className="btn btn-login "
              data-mode="signin"
            >
              <i className="fas fa-sign-out-alt" />
            </a>
          </div>
        </div>
      </div>
      <br />
      <br />
    </div>
  );
};

LoggedInNavBar.propTypes = {
  handleSearch: PropTypes.func,
  handleSearchChange: PropTypes.func
};

Courses component

import React, { Fragment } from "react";
import { details } from "../../routes/protectedRoutes";
import { AUTHENTICATED } from "../../utils/myHeaders";
import { ViewAllCourses } from "../../views/courses/viewSearchResults";
import { retrieveAllCourses } from "../../actions/courseActions/courseAction";
import { LoggedInNavBar } from "../navigation/LoggedIn";
import { API_URLS } from "../../appUrls";
import PropTypes from "prop-types";
import { connect } from "react-redux";

export class Courses extends React.Component {
  constructor(props) {
    super(props);
    this.user = details(AUTHENTICATED);
    this.state = {
      search: ""
    };
  }

  componentDidMount() {
    this.fetchCourses();
  }

  fetchCourses = (searchStr = null) => {
    let Url = API_URLS.FETCH_CREATE_ARTICLES;
    const { search } = this.state;
    const query = searchStr !== null ? searchStr : search;
    this.props.dispatch(retrieveAllCourses( Url, query ));
  };

  handleSearch = search => {
    this.setState({ search });
    this.fetchCourses(search);
  };

  handleSearchChange = search => {

    if (!search) {
      this.setState({ search });
      this.fetchCourses(search);
    }
  };

  render() {
    const { allCourses } = this.props;


    return (
        <div>

        <LoggedInNavBar
          handleSearchChange={this.handleSearchChange}
          handleSearch={this.handleSearch}
        />

        <ViewAllCourses results={allVideos} />
        </div>

    );

  }
}

Courses.propTypes = {
  dispatch: PropTypes.func.isRequired,
  allCourses: PropTypes.array
};

const mapStateToProps = state => ({
  allCourses: state.coursesReducer.courses
});
const mapDispatchToProps = dispatch => ({ dispatch });

export default connect(
  mapStateToProps,
  mapDispatchToProps
)(Courses);

API responseenter image description here

Upvotes: 0

Views: 56

Answers (1)

Avanthika
Avanthika

Reputation: 4182

  1. In your reducer, why are you concatenating it with previous state results? Your filtering will never show associated data if you're doing this.

  2. I see no payload.results in your action dispatch. Shouldn't it be action.payload & not action.payload.results?

return {
  ...state,
  courses: action.payload,
}

There's no state variable called videos in your reducer. You're dispatching & storing courses, so you have to listen to:

const mapStateToProps = state => ({
  allCourses: state.coursesReducer.courses
});

Hope this is helpful!

Upvotes: 1

Related Questions