简体   繁体   中英

Clicking on a child element calls a parent event listener in React

I am making a search input where people can look for skills and then choose a skill from the choices. However, when I click on any of the options, it calls the event listener for the parent element ( onBlur={() => setIsFocus(false)} ). When I remove this event listener, it adds a new skill, but when it is there, it does not run the function that is attached to the child element.

I tried to add e.stopPropagtion() to the child element, but it didn't work.

My Code:

import { useRef, useState } from "react"

import fetchSkills from "../api"
import BaseInput from "../components/reusable/BaseInput"
import SkillsSuggestionList from "../components/solutions/SkillsSuggestionList"
import Icons from "../components/SvgIcons/Icons"
import { debounce } from "../utils/shared"

const SKILLS = [
  { name: "html" },
  { name: "css" },
  { name: "javascript" },
  { name: "bootstrap" },
  { name: "tailwind-css" },
  { name: "react" },
  { name: "vue" },
  { name: "angular" },
]

const SolutionForm = () => {
  const [skills, setSkills] = useState([])
  const [isFocus, setIsFocus] = useState(false)
  const selectRef = useRef(null)

  // fetch skills and render them in a SkillsSuggestionList component
  const handleSelectChange = async (e) => {
    const { value } = e.target

    if (value.length > 2) {
      const result = await fetchSkills(value)
      setSkills(result.items)
    }
  }

  const addSkill = (skill) => {
      setData({
        ...data,
        skills: [...data.skills, skill],
      })
    setIsFocus(false)
    setSkills([])
    selectRef.current.value = ""
  }

  return (
    <div>
      <div>
        <form onSubmit={handleSubmit}>
              <div
                key={input.name}
                onBlur={() => setIsFocus(false)} // this is a parent event listener
              >
                <div
                  key={input.name}
                >
                  <ul>
                    {data.skills.map((skill, index) => (
                      <li key={index}>
                          {skill}
                      </li>
                    ))}
                  </ul>
                  <BaseInput
                    onChange={debounce(handleSelectChange, 500)}
                    onFocus={() => setIsFocus(true)}
                    name={input.name}
                    innerRef={selectRef}
                  />
                </div>
                {isFocus && (
                  <SkillsSuggestionList
                    skills={skills.length > 0 ? skills : SKILLS}
                    addSkill={addSkill}
                  />
                )}
              </div>
        </form>
      </div>
    </div>
  )
}

export default SolutionForm

SkillSuggestionList component:

const SkillsSuggestionList = ({ skills, addSkill }) => {
  return (
    <ul
      id="skills"
    >
      {skills.map((skill) => (
        <li
          onClick={(e) => { // this is a child event listener
            e.stopPropagation()
            addSkill(skill.name)
          }}
          key={skill.name}
        >
          {skill.name}
        </li>
      ))}
    </ul>
  )
}

export default SkillsSuggestionList

Using onMouseDown instead of onClick will do the trick.

{skills.map((skill) => (
        <li
          onMouseDown={(e) => { // this is a child event listener
            // e.stopPropagation() <--- this won't be needed
            addSkill(skill.name)
          }}
          key={skill.name}
        >
          {skill.name}
        </li>
      ))}

Also, let me explain why onMouseDown is needed here. When you click the list, before onClick run it blurs down and the function is no longer available. So when you use onMouseDown it will first run the addSkill function and then blur it. Hop it will work.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM