1

I am new to ReactJS and I am trying to create a search feature with react by fetching data from multiple API. Below is the Search.js file. I tried so many times to make it functions and make the results appear live while typing. I however keep on getting this error message TypeError: values.map is not a function. Where am I going wrong and how do I fix it?

function Search() {
  const [input, setInput] = useState("");
  const [results, setResults] = useState([]);

  const urls = [
    'https://jsonplaceholder.typicode.com/posts/1/comments',
    'https://jsonplaceholder.typicode.com/comments?postId=1'
  ]

  Promise.all(urls.map(url => fetch(url)
    .then((values) => Promise.all(values.map(value => value.json())))
    .then((response) => response.json())
    .then((data) => setResults(data))
    .catch(error => console.log('There was a problem!', error))

  ), [])

  const handleChange = (e) => {
    e.preventDefault();
    setInput(e.target.value);
  }
  if (input.length > 0) {
    results.filter((i) => {
      return i.name.toLowerCase().match(input);
    })
  }
  return ( <
    div className = "search"
    htmlFor = "search-input" >
    <
    input type = "text"
    name = "query"
    value = {
      input
    }
    id = "search"
    placeholder = "Search"
    onChange = {
      handleChange
    }
    /> {
    results.map((result, index) => {
        return ( <
            div className = "results"
            key = {
              index
            } >
            <
            h2 > {
              result.name
            } < /h2> <
            p > {
              result.body
            } < /p> {
            result
          } <
          /div>
      )
    })

} </div>

)
}
.search {
  position: relative;
  left: 12.78%;
  right: 26.67%;
  top: 3%;
  bottom: 92.97%;
}

.search input {
  /* position: absolute; */
  width: 40%;
  height: 43px;
  right: 384px;
  margin-top: 50px;
  top: 1.56%;
  bottom: 92.97%;
  background: rgba(0, 31, 51, 0.02);
  border: 1px solid black;
  border-radius: 50px;
  float: left;
  outline: none;
  font-family: 'Montserrat', sans-serif;
  font-style: normal;
  font-weight: normal;
  font-size: 16px;
  line-height: 20px;
  /* identical to box height */
  display: flex;
  align-items: center;
  /* Dark */
  color: #001F33;
}


/* Search Icon */

input#search {
  background-repeat: no-repeat;
  text-indent: 50px;
  background-size: 18px;
  background-position: 30px 15px;
}

input#search:focus {
  background-image: none;
  text-indent: 0px
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.6.3/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.6.3/umd/react-dom.production.min.js"></script>

3
  • Try console.loging it with then(res => console.log(res)) Commented Jan 4, 2021 at 6:53
  • Can you log what your response JSON object shape is? Commented Jan 4, 2021 at 6:57
  • I tried and this was the response There was a problem! TypeError: values.map is not a function at Search.js:26 at async Promise.all (:3001/index 0) Commented Jan 4, 2021 at 7:05

1 Answer 1

2

Issue

  1. The response you get from fetch(url) is just the one single response, so there's nothing to map.
  2. The data fetching occurs in the function body of the component, so when working this causes render looping since each render cycle fetches data and updates state.
  3. The input.length > 0 filtering before the return does nothing since the returned filtered array isn't saved, and it also incorrectly searches for sub-strings.
  4. Attempt to render result object in the render function, objects are invalid JSX

Solution

  1. Skip the .then((values) => Promise.all(values.map((value) => value.json()))) step and just move on to accessing the JSON data.
  2. Move the data fetching into a mounting useEffect hook so it's run only once.
  3. Move the filter function inline in the render function and use string.prototype.includes to search.
  4. Based on other properties rendered and what is left on the result object I'll assume you probably wanted to render the email property.

Code:

function Search() {
  const [input, setInput] = useState("");
  const [results, setResults] = useState([]);

  useEffect(() => {
    const urls = [
      "https://jsonplaceholder.typicode.com/posts/1/comments",
      "https://jsonplaceholder.typicode.com/comments?postId=1"
    ];

    Promise.all(
      urls.map((url) =>
        fetch(url)
          .then((response) => response.json())
          .then((data) => setResults(data))
          .catch((error) => console.log("There was a problem!", error))
      ),
      []
    );
  }, []);

  const handleChange = (e) => {
    e.preventDefault();
    setInput(e.target.value.toLowerCase());
  };

  return (
    <div className="search" htmlFor="search-input">
      <input
        type="text"
        name="query"
        value={input}
        id="search"
        placeholder="Search"
        onChange={handleChange}
      />
      {results
        .filter((i) => i.name.toLowerCase().includes(input))
        .map((result, index) => {
          return (
            <div className="results" key={index}>
              <h2>{result.name}</h2>
              <p>{result.body}</p>
              {result.email}
            </div>
          );
        })}
    </div>
  );
}

Edit how-do-i-fetch-search-results-from-multiple-api-using-reactjs

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

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.