首页 > 解决方案 > 在 React 中按类名获取最近的父元素

问题描述

使用 React,我如何使用某个类名引用父元素最近的元素?

下面的代码:

const Skill = props => (
<div className="skill-card">
    <div className="skill-header">
        <div className="skill-header-text">
            <div className="skill-header-img-container">
                <img src={require('../../img/projects/skills/' + props.skill.image)} alt={props.skill.name} className="skill-image" />
            </div>
            <div className="skill-heading"><h2 className="skill-name">{props.skill.name}</h2></div>
        </div>
        <div className="skill-header-icon">
            <FontAwesomeIcon icon={"angle-" + props.angle} onClick={props.click} />
        </div>
    </div>
    <div className="skill-body">
        ...
    </div>
</div>
);

class Skills extends Component {
    ...

    handleClick = () => {
        // Add style to the closest skill-body in here!
    }
}

在这种情况下,一旦我单击FontAwesomeIcon,我希望打开div带有 className 的元素。skill-body

首先我认为使用状态会很好,但到目前为止,所有skill-body元素都会在单击FontAwesomeIcon. 我的方法只是向skill-body(display: blockdisplay: none) 添加样式。

我怎样才能用 ? 引用skill-body里面的Component's 函数handleClick

编辑:使用了 HMR 的功能组件示例,但我仍然卡住了。

const Skills = ({ skills }) => (
<div>
    {skills.map(skill => (
        <SkillContainer key={skill._id} skill={skill} />
    ))}
</div>
);

const Skill = ({ skill, open, toggle, data }) => (
    <div>
        <h4 onClick={toggle}>skill: {skill} {data.name}</h4>
        {open && <div>{data.description}</div>}
    </div>
);

const SkillContainer = ({ skill }) => {
    const [open, setOpen] = React.useState(false);
    const [data, setData] = React.useState({ skills: [] });

    const toggle = React.useCallback(() => setOpen(open => !open), []);

    useEffect(() => {
        const fetchData = async () => {
            const result = await         
                axios("http://localhost:5000/api/skills/");

        setData(result.data);
    }

    fetchData();
}, []);

return React.useMemo(() => Skill({ skill, open, toggle, data }), [open, 
skill, toggle, data]);
}

export default Skills;

useHook通过阅读文档到此为止。我想做的是收集数据,axios然后用它设置内容。

标签: javascriptreactjsaxios

解决方案


您可以在功能组件中使用 State,如下所示:

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>

类版本有点冗长:

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>

该代码使用条件渲染,但您也可以使用 props 来设置 className


推荐阅读