Skip to content Skip to sidebar Skip to footer

Get Closest Parent Element By Class Name In React

Using React, how do I refer to parent element's closest element with certain classname? Code below: const Skill = props => (

Solution 1:

You can use State in functional components like so:

const Skills = ({ skills, loading }) =>
  loading ? (
    'loading'
  ) : (
    <div>
      {skills.map(skill => (
        <SkillContainer key={skill._id} skill={skill} />
      ))}
    </div>
  )

const Skill = ({ skill, open, toggle }) => (
  <div>
    <h4 onClick={toggle}>
      skill: {skill.completed} {skill.id}
    </h4>
    {open && <div>{skill.title}</div>}
  </div>
)

const SkillContainer = ({ skill }) => {
  const [open, setOpen] = React.useState(false)
  const toggle = React.useCallback(
    () => setOpen(open => !open),
    []
  )
  return React.useMemo(
    () => Skill({ skill, open, toggle }),
    [open, skill, toggle]
  )
}
//savety to not set state when component is no longer mounted
const useIsMounted = () => {
  const isMounted = React.useRef(false)
  React.useEffect(() => {
    isMounted.current = true
    return () => (isMounted.current = false)
  }, [])
  return isMounted
}

const SkillsContainer = () => {
  const [result, setResult] = React.useState({
    loading: true,
    data: []
  })
  const isMounted = useIsMounted()
  React.useEffect(() => {
    const fetchData = () => {
      //cannot use async await here because Stack Overflow
      //  uses old babel
      axios
        .get('https://jsonplaceholder.typicode.com/todos')
        .then(result => {
          if (isMounted.current) {
            //do not set data if component is no longer mounted
            setResult({
              loading: false,
              data: result.data
            })
          }
        })
    }
    fetchData()
  }, [isMounted])

  return React.useMemo(
    () =>
      Skills({
        skills: result.data,
        loading: result.loading
      }),
    [result]
  )
}

//render app
ReactDOM.render(
  <SkillsContainer />,
  document.getElementById('root')
)
<script src="https://cdnjs.cloudflare.com/ajax/libs/axios/0.19.2/axios.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.8.4/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.8.4/umd/react-dom.production.min.js"></script>
<div id="root"></div>

The class version is a little more verbose:

class SkillsContainer extends React.PureComponent {
  state = {
    loading: true,
    //should do error here as well
    result: []
  }
  fetchData = (
    length //arrow function auto bind to this
  ) =>
    new Promise(r =>
      setTimeout(() => r(length), 2000)
    ).then(l =>
      this.setState({
        loading: false,
        result: [...new Array(l)].map((_, i) => i+1)
      })
    )
  //if your skills array changes based on props:
  // example here: https://reactjs.org/docs/react-component.html#componentdidupdate
  componentDidUpdate(prevProps) {
    if (this.props.length !== prevProps.length) {
      this.fetchData(this.props.length)
    }
  }
  //fetch data on mount
  componentDidMount() {
    this.fetchData(this.props.length)
  }
  render() {
    return this.state.loading
      ? 'loading'
      : Skills({ skills: this.state.result })
  }
}
const Skills = ({ skills }) => (
  <div>
    {skills.map((skill, id) => (
      <SkillContainer key={id} skill={skill} />
    ))}
  </div>
)
const Skill = ({ skill, open, toggle }) => (
  <div>
    <h4 onClick={toggle}>skill: {skill}</h4>
    {open && <div>extra</div>}
  </div>
)

class SkillContainer extends React.PureComponent {
  state = {
    open: false
  }
  toggle() {
    this.setState({
      open: !this.state.open
    })
  }
  render() {
    return Skill({
      skill: this.props.skill,
      open: this.state.open,
      toggle: this.toggle.bind(this)
    })
  }
}
//render app
ReactDOM.render(
  <SkillsContainer length={2} />,
  document.getElementById('root')
)
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.8.4/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.8.4/umd/react-dom.production.min.js"></script>
<div id="root"></div>

The code uses conditional rendering but you can use props to set className as well


Solution 2:

I would like to pass the skill body element ref to the Parent so that you can handle the style for the element. Hope the below code would done the thing which you need.

class Skill extends React.Component {

    render() {
        return (
            <div className="skill-card">
            <div className="skill-header">
                <div className="skill-header-icon">
                   <div onClick={() => this.props.click(this.skillBodyEle)}>Header Icon</div>
                </div>
            </div>
            <div
            ref={e => this.skillBodyEle = e}
            className="skill-body">
                Skill Body
            </div>
        </div>
            );
    }
}

class Skills extends React.Component {
    handleClick = (skillBodyEle) => {
        if (skillBodyEle.hidden) {
            skillBodyEle.hidden = false;
        } else {
            skillBodyEle.hidden = true;
        }
      };
      render() {
          return (
          <Skill 
            click={this.handleClick} 
          />
          );
      }
}

Post a Comment for "Get Closest Parent Element By Class Name In React"