0

I have two inputs and am trying to have them filter out separate data. I'm using an NPM package for the filter which works great for the first input, as it returns based on any criteria on the card. I need the second input to return based on tags that are added after the fact. I feel like this is a simple answer but I'm a bit new so I can't get this to work. I've tried using context to get the state from any component but either way, I do this I still can't get it to filter. Any feedback or help would be greatly appreciated - thanks :)

Main.jsx

import React from "react";
import axios from "axios";
import { useState, useEffect, useContext } from "react";
import TagProvider from "../context/TagContext";
import { TagContext } from "../context/TagContext";
import Card from "./Card/Card";
import "./Main.scss";
import FilterResults from "react-filter-search";

export default function Main() {
  //using state with hooks
  const [students, setStudents] = useState([]);
  const [value, setValue] = useState("");
  const [valueTwo, setValueTwo] = useContext(TagContext);

  // axios call to get data
  const url = "https://api.hatchways.io/assessment/students";
  const getData = () => {
    axios
      .get(`${url}`)
      .then((res) => {
        const studentData = res.data.students;
        setStudents(studentData);
      })
      .catch((error) => console.log(`Error: ${error}`));
  };
  useEffect(() => {
    getData();
  }, []);

  // handleChange function for filter results

  let handleChange = (event) => {
    const { value } = event.target;
    setValue(value);
  };

  let handleChangeTwo = (event) => {
    const { valueTwo } = event.target;
    setValueTwo(valueTwo);
  };

  return (
    <>
      <TagProvider>
        <div className="main">
          <input
            type="text"
            className="main__search"
            value={value}
            placeholder="Search by name"
            onChange={handleChange}
          />
          <input
            type="text"
            className="main__search"
            value={valueTwo}
            placeholder="Search by tag"
            onChange={handleChangeTwo}
          />

          <FilterResults
            value={value}
            data={students}
            renderResults={(results) => (
              <div className="main__container">
                {results.map((item) => (
                  <Card
                    key={item.id}
                    email={item.email}
                    company={item.company}
                    skill={item.skill}
                    firstname={item.firstName}
                    lastname={item.lastName}
                    grades={item.grades}
                    pic={item.pic}
                  />
                ))}
              </div>
            )}
          />
        </div>
      </TagProvider>
    </>
  );
}

Card.jsx

import React, { useState, useRef } from "react";
import "./Card.scss";
import Tags from "../Tags/Tags";
import TagProvider from "../../context/TagContext";

export default function Card(props) {
  const [setActive, setActiveState] = useState("");
  const [setHeight, setHeightState] = useState("0px");
  const content = useRef(null);

  function toggleAccordion() {
    setActiveState(setActive === "" ? "active" : "");
    setHeightState(
      setActive === "active" ? "0px" : `${content.current.scrollHeight}px`
    );
  }
  // convert string to integer
  let result = props.grades.map((x) => {
    return parseInt(x, 10);
  });

  // caluclate average function
  const average = (nums) => nums.reduce((a, b) => a + b) / nums.length;

  const gradeTitle = [
    "Test 1:",
    "Test 2:",
    "Test 3:",
    "Test 4:",
    "Test 5:",
    "Test 6:",
    "Test 7:",
    "Test 8:",
  ];

  return (
    <div>
      {/* Card Section */}
      <div className="card__container">
        <img className="card__image" src={props.pic} alt="avatar" />
        <div className="card__container-two">
          <div className="card__container-plus">
            <h1 className="card__name">
              {props.firstname} {props.lastname}
            </h1>
            <button className="card__button" onClick={toggleAccordion}>
              &#43;
            </button>
          </div>
          <div className="card__container-three">
            <div className="card__info">Email: {props.email}</div>
            <div className="card__info">Company: {props.company}</div>
            <div className="card__info">Skill: {props.skill}</div>
            <div className="card__info">Average: {average(result)}%</div>
          </div>
        </div>
      </div>

      <TagProvider>
        <Tags />
      </TagProvider>

      {/* Card Expansion */}
      <div className="accordion">
        <div
          ref={content}
          style={{ maxHeight: `${setHeight}` }}
          className="accordion__content"
        >
          <div className="accordion__tests">
            <div className="accordion__test-title">
              {gradeTitle.map((title) => (
                <div>{title}</div>
              ))}
            </div>
            <div className="accordion__test-results">
              {result.map((number) => (
                <li>{number}%</li>
              ))}
            </div>
          </div>

          <div
            className="accordion__text"
            dangerouslySetInnerHTML={{ __html: props.content }}
          />
        </div>
      </div>
    </div>
    // </TagContext.Provider>
  );
}

Tags.jsx

import React, { useContext } from "react";
import { X } from "react-feather";
import "./Tags.scss";
import { TagContext } from "../../context/TagContext";
import TagProvider from "../../context/TagContext";

export default function Tags() {
  const [tags, setTags] = useContext(TagContext);

  return (
    <TagProvider>
      <div className="tags">
        <ul className="tags__list">
          {tags.map((tag) => (
            <li className="tags__tag">
              {tag}
              <X
                className="tags__close-icon"
                size="16"
                onClick={() => {
                  setTags([...tags.filter((word) => word !== tag)]);
                }}
              />
            </li>
          ))}
        </ul>

        <div className="tags__form">
          <input
            className="tags__input"
            type="text"
            placeholder="Add a tag.."
            onKeyPress={(event) => {
              if (event.key === "Enter") {
                setTags([...tags, event.target.value]);
                event.target.value = "";
              }
            }}
            autofocus
          />
        </div>
      </div>
    </TagProvider>
  );
}

TagContext.js

import { useState, createContext } from "react";

export const TagContext = createContext();

const TagProvider = (props) => {
  const [valueTwo, setValueTwo] = useState([]);
  const [tags, setTags] = useState([]);

  return (
    <TagContext.Provider value={[valueTwo, setValueTwo, tags, setTags]}>
      {props.children}
    </TagContext.Provider>
  );
};

export default TagProvider;
2
  • Can you provide more details, things are 50% clear from your description? Commented Jun 19, 2021 at 6:13
  • Remvove all TagProvider from all *.js files, except for main.js. That should solve the problem. Commented Jun 20, 2021 at 21:05

1 Answer 1

1

I don't know what do you have in context/TagContext, but normally, one provider should be used only once, and should be placed wrapping the Childs that will be subscribe to that context, and you are using the TagProvider in Main.js, Tag.js and Card.js, you should keep only the TagProvider that is in Main.js file. After removing additional providers, the next change to do is pass through that provider the values, by putting the value prop on it: <TagProvider value={[valueTwo, setValueTwo]}>. Whit this code we pass an array that in the first position will have the valueTwo and in the second position the setValueTwo function. After that, any component that be inside of the provider (no matter the depth level) can subscribe to that context and use the values. So in Tag.js, when you use this code: useContext(TagContext); it will resolve into an array that will have what you pass through the provider value, and that will be an array with the form of: [valueTwo, setValueTwo].

These answer is based on what you should have on context/TagContext so it will be nice if you share that code too.

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

1 Comment

Updated with TagContext!

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.