Home > database >  I want to pass data to parent component foom a child component using props
I want to pass data to parent component foom a child component using props

Time:01-27

I'm trying to pass data to the parent component Top.js using props from a child component TagsInput.js where I can add tags but I don't understand what is causing the error...

What I want to achieve

I want to pass "tags" to the parent component Top.js from TagsInput.js in the child component with props.

I got the error like

props.setTagsinput is not a function

enter image description here

TagsInput.js

import React from "react";

const TagsInput = (props) => {

    //1, Define the tags variable to store the entered tags. (I want to pass the value of the tags variable to the parent component Top.js)
    const [tags, setTags] = React.useState([]);

    //2, Put formText in the function received from the parent component and return it.
    props.setTagsinput(tags);
    console.log(props)

    let tag_list = []
    tag_list.push(tags);


    const addTags = event => {
      if (event.key === "Enter" && event.target.value !== "") {
          setTags([...tags, event.target.value]);
          event.target.value = "";
      }
    };
    const removeTags = index => {
      setTags([...tags.filter(tag => tags.indexOf(tag) !== index)]);
    };

    return (
        <div className="tags-input">
            <div className="tags_section">
                {tags.map((tag, index) => (
                    <div className="tag tag-flex" key={index}>
                            <p className="tag-p">{tag}</p>
                    </div>
                ))}
            </div>
            <input
                type="text"
                onKeyUp={event => addTags(event)}
                placeholder="Press enter to add tags"
            />
        </div>
    );
};
export default TagsInput;

Top.js

import React, {useState, useEffect} from 'react';
import axios from 'axios';
import Student from './Student';
import TagsInput from "./TagsInput";

const Top = () => {
  const [ posts, setPosts] = useState([]);
  const [ allPosts, setAllPosts] = useState([]);

  let tag_list = []
  const [searchKeyword, setSearchKeyword] = React.useState("");
  const [searchTagKeyword, setTagSearchKeyword] = React.useState("");
  console.log(searchKeyword)


  const[tags_from_tagsinput, setTagsinput]= useState("");
  console.log(tags_from_tagsinput);

  useEffect(() => {
    axios.get('xxx.com')
    .then(result => {
      setPosts(result.data.students);
      setAllPosts(result.data.students);
      if (searchKeyword) {
        getSearchResult()
      }
    })},
    [searchKeyword]);



  const getSearchResult = () => {
    console.log(searchKeyword)
    const result = allPosts.filter((output, index) => {
      return output.firstName.toLowerCase().includes(searchKeyword.toLowerCase())||output.lastName.toLowerCase().includes(searchKeyword.toLowerCase());
    });
    console.log(result)
    setPosts(result);
  };


  
  const getTagSearchResult = () => {
    console.log(searchTagKeyword)
    const result = allPosts.filter((output, index) => {
      return output.lastName.toLowerCase().includes(searchTagKeyword.toLowerCase());
    });
    console.log(result)
    setPosts(result);
  };

  return (
    <div>
      <TagsInput setTagsinput={setTagsinput}/>
      <div>
      <input className="search-box" placeholder="" value={searchKeyword} onChange={(e) => setSearchKeyword(e.target.value)}/>
      </div>
      <div>
      <input className="search-box" placeholder="" value={searchTagKeyword} onChange={(e) => setSearchKeyword(e.target.value)}/>
      </div>
      <div>
      {searchKeyword &&
      <p>{searchKeyword} Search</p>
      }
      {posts ?
      <>
        {posts.map((data, i) =>
          <Student data={data} />
        )}
      </>
      :
      <div>
        <p>Not Found!</p>
      </div>
      }
      </div>
    </div>
  );
}
export default Top;

Student.js

import React, {useState} from 'react';
import TagsInput from './TagsInput';

const Student = (props) => {
const [show, setShow] = useState(false)

  const gradesAverage = (grades) => {
    let sum = 0;
    grades.forEach(function(score) {
      sum  = Number(score);
    });
    let ave = sum / grades.length
    return ave;
  };

  return (
    <div className="flex">
        <div className="image">
            <img src={props.data.pic} className="profile" />
        </div>
        <div>
            <p className="name">{props.data.firstName} {props.data.lastName}</p>
            <button className="button" onClick={() => setShow(!show)}>
                {show? <div className="button_p">-</div>:<div className="button_p"> </div>}
            </button>
            <div className="info">
                <p>Email: {props.data.email}</p>
                <p>Company: {props.data.company}</p>
                <p>Skill: {props.data.skill}</p>
                <p>Average Grade: {gradesAverage(props.data.grades)}%</p>
                {show &&
                    <>
                        <p>Test 1: {props.data.grades[0]}%</p>
                        <p>Test 2: {props.data.grades[1]}%</p>
                        <p>Test 3: {props.data.grades[2]}%</p>
                        <p>Test 4: {props.data.grades[3]}%</p>
                        <p>Test 5: {props.data.grades[4]}%</p>
                        <p>Test 6: {props.data.grades[5]}%</p>
                        <p>Test 7: {props.data.grades[6]}%</p>
                        <p>Test 8: {props.data.grades[7]}%</p>
                    </>
                }
                <TagsInput />
            </div>
        </div>
    </div>
  );
}
export default Student;

CodePudding user response:

You can not directly use one component hook declaration in another component, you need to have a callback function to update that state. I modified your code to use the top page setTagsinput in student tag input

Top.js

import React, { useState, useEffect } from "react";
import axios from "axios";
import Student from "./Student";
import TagsInput from "./TagsInput";

const Top = () => {
  const [posts, setPosts] = useState([]);
  const [allPosts, setAllPosts] = useState([]);

  let tag_list = [];
  const [searchKeyword, setSearchKeyword] = React.useState("");
  const [searchTagKeyword, setTagSearchKeyword] = React.useState("");
  console.log(searchKeyword);

  const [tags_from_tagsinput, setTagsinput] = useState("");
  console.log(tags_from_tagsinput);

  useEffect(() => {
    axios.get("xxx.com").then((result) => {
      setPosts(result.data.students);
      setAllPosts(result.data.students);
      if (searchKeyword) {
        getSearchResult();
      }
    });
  }, [searchKeyword]);

  const getSearchResult = () => {
    console.log(searchKeyword);
    const result = allPosts.filter((output, index) => {
      return (
        output.firstName.toLowerCase().includes(searchKeyword.toLowerCase()) ||
        output.lastName.toLowerCase().includes(searchKeyword.toLowerCase())
      );
    });
    console.log(result);
    setPosts(result);
  };

  const getTagSearchResult = () => {
    console.log(searchTagKeyword);
    const result = allPosts.filter((output, index) => {
      return output.lastName
        .toLowerCase()
        .includes(searchTagKeyword.toLowerCase());
    });
    console.log(result);
    setPosts(result);
  };

  const setTagsFromStudent = (tags) => {
    setTagsinput(tags);
  };

  return (
    <div>
      <div>
        <input
          className="search-box"
          placeholder=""
          value={searchKeyword}
          onChange={(e) => setSearchKeyword(e.target.value)}
        />
      </div>
      <div>
        <input
          className="search-box"
          placeholder=""
          value={searchTagKeyword}
          onChange={(e) => setSearchKeyword(e.target.value)}
        />
      </div>
      <div>
        {searchKeyword && <p>{searchKeyword} Search</p>}
        {posts ? (
          <>
            {posts.map((data, i) => (
              <Student data={data} setStudentTags={setTagsFromStudent} />
            ))}
          </>
        ) : (
          <div>
            <p>Not Found!</p>
          </div>
        )}
      </div>
    </div>
  );
};
export default Top;

Student.js

import React, { useState } from "react";
import TagsInput from "./TagsInput";

const Student = (props) => {
  const [show, setShow] = useState(false);

  const gradesAverage = (grades) => {
    let sum = 0;
    grades.forEach(function (score) {
      sum  = Number(score);
    });
    let ave = sum / grades.length;
    return ave;
  };

  return (
    <div className="flex">
      <div className="image">
        <img src={props.data.pic} className="profile" />
      </div>
      <div>
        <p className="name">
          {props.data.firstName} {props.data.lastName}
        </p>
        <button className="button" onClick={() => setShow(!show)}>
          {show ? (
            <div className="button_p">-</div>
          ) : (
            <div className="button_p"> </div>
          )}
        </button>
        <div className="info">
          <p>Email: {props.data.email}</p>
          <p>Company: {props.data.company}</p>
          <p>Skill: {props.data.skill}</p>
          <p>Average Grade: {gradesAverage(props.data.grades)}%</p>
          {show && (
            <>
              <p>Test 1: {props.data.grades[0]}%</p>
              <p>Test 2: {props.data.grades[1]}%</p>
              <p>Test 3: {props.data.grades[2]}%</p>
              <p>Test 4: {props.data.grades[3]}%</p>
              <p>Test 5: {props.data.grades[4]}%</p>
              <p>Test 6: {props.data.grades[5]}%</p>
              <p>Test 7: {props.data.grades[6]}%</p>
              <p>Test 8: {props.data.grades[7]}%</p>
            </>
          )}
          {/*pass settag from topTag component*/}
          <TagsInput setStudentTags={props.setStudentTags} />
        </div>
      </div>
    </div>
  );
};
export default Student;

TagsInput.js

import React from "react";

const TagsInput = (props) => {
  const [tags, setTags] = React.useState([]);

  let tag_list = [];
  tag_list.push(tags);

  const addTags = (event) => {
    if (event.key === "Enter" && event.target.value !== "") {
      setTags([...tags, event.target.value]);
      // call function pass down from toptag
      props.setStudentTags(tags);
      event.target.value = "";
    }
  };
  const removeTags = (index) => {
    setTags([...tags.filter((tag) => tags.indexOf(tag) !== index)]);
  };

  return (
    <div className="tags-input">
      <div className="tags_section">
        {tags.map((tag, index) => (
          <div className="tag tag-flex" key={index}>
            <p className="tag-p">{tag}</p>
          </div>
        ))}
      </div>
      <input
        type="text"
        onKeyUp={(event) => addTags(event)}
        placeholder="Press enter to add tags"
      />
    </div>
  );
};
export default TagsInput;

CodePudding user response:

You should consider exploring React context -https://reactjs.org/docs/context.html, its built exactly for something like this.

You are getting this error because, like you mentioned, TagsInput component is used in Student component but it doesn’t pass the state setter setTagsInput function to the TagsInput component. Now, assuming you need tags created inside Student and displayed in Top, also assuming that both are rendered in the same parent component, you can create a state for tags in the parent component. This component will pass a state setter function to Student which passes the setter to TagsInput and the state itself to Top to use the list of tags. Something like:

const App = () => {
    const [tags,setTags] = useState([]);
    return (<div>
        <Top tags={tags} />
        <Student setTags={setTags} />
    </div>);
}

Your Student component can then pass it to TagsInput like:

const Student = (props) => {
   return (<div>
   {/* everything else */}
   <TagsInput setTagsinput={props.setTags} />
   </div>)
}

CodePudding user response:

In your Top component you can create a function that updates your tags_from_tagsinput hook then pass it as props to the child component

import TagsInput from "./TagsInput";
const Top = () => {

  const[tags_from_tagsinput, setTagsinput]= useState("");
  console.log(tags_from_tagsinput);

  const getTag = (value) => {
    setTagsinput(value);
  };


  return (
    <div>
      <TagsInput  getTag={getTag} />
    </div>
  );
}
export default Top;

Now from your TagsInput component you can call this function to update tags_from_tagsinput of Top, let's suppose that you want to updated when the user click on a button

import React from "react";

const TagsInput = (props) => {

    return (
        <div className="tags-input">
         ...
        <button onClick={()=>{props.getTag(tags)}}>updated parent component</button>
        </div>
    );
};
export default TagsInput;
  • Related