React - 父类组件功能改变子组件的类

Posted

技术标签:

【中文标题】React - 父类组件功能改变子组件的类【英文标题】:React - parent class component function change class of child component 【发布时间】:2021-04-06 16:31:08 【问题描述】:

我有两个组件,一个父组件和它的子组件。

在父组件中,我为“活动”存储了一个状态,该状态包含活动子组件的 ID。我想做的是有一个handleClick函数,它将被点击的子组件的id与'active'的值进行比较,如果它相同或不同,我希望它更新html className子组件实现一定的样式效果(可能包括一些动画)。

问题: 有没有办法以特定子元素的 className 为目标并对其进行更新?

在父组件的状态中存储“活动”子组件的 id 时,在子组件本身中处理此函数是否更好?

如果我希望基于子组件的类名从一个类名更改为另一个类名来实现 css 动画,是否还有其他考虑因素,例如包括动画在组件的渲染上运行,如果我是希望以这种方式动画变化?

我确信还有其他方法可以解决这个问题,我完全愿意接受有关实现上述目标的最佳方法的建议,但我还要包括我刚刚开始使用 react 并且还没有学习关于如何使用钩子呢。我仍在使用基本的功能和基于类的组件。

提前致谢,示例代码和下面的伪代码。

示例父组件:

import React, Component from "react";
import Task from './openTasks';
import TasksData from './tasks-data';
        
        
    class openTaskAccordion extends Component
            
      constructor()
           super()
           this.state = 
                        //holds the id of the currently opened/active item but initialized to -1 since there is no item with an id of -1 at initialization.
                active: -1 
           
        this.handleClick() = this.handleClick.bind(this);
      
            
     handleClick()
        //if (the id of the clicked task === this.state.active)
        //   change the className of the clicked child component to "closed"
        //     else 
        //   change the className of the child component with id == this.state.active to "closed", change the className of the clicked child component to "open" and update this.state.active to the id of the now open child component with setState.
        // 
     
        
            
     render()
            
                    const Tasks = TasksData.map(task=> <Task key=task.id task =task/>)
                        return(
                            Tasks
                        )
     
            
            
                
 
            
     export default openTaskAccordion  

示例子组件

import React from "react";
import "./OpenTasks.css"

function openTasks()

        return (
            <div id = props.task.id className="tasks" value = props.task.id>
                <h1 >props.task.clientName</h1>
                <div  className="accordion-item accordion-item-closed" >
                    <h2>props.task.matter</h2>
                    <p> - props.task.matterStatus</p>
                </div>
            </div>
        );


export default openTasks

【问题讨论】:

【参考方案1】:

问题

父组件

    this 未在 handleClick 的构造函数中正确绑定。

子组件。

    openTasks 是一个函数组件,所以没有 this,或者更确切地说,this 只是未定义。 openTasks 不消耗任何传递给它的道具。

回答问题

有没有办法以特定子元素的 className 为目标并对其进行更新?

你可以这样做,但是直接 DOM 操作并进入其他组件来改变事情在 React 中是反模式的。 React 方式是将数据作为 props 传递(数据包括什么是“活动”或不“活动”)并让子组件在本地处理它。

在子进程中处理这个函数会更好吗? 将“活动”子项的 id 存储在父项的状态中 组件?

不,我不这么认为,父母应该存储有关当前“活跃”孩子的单一事实来源。将 props 传递给子级,包括子级可以/应该调用以更新父级中的状态的任何回调。

解决方案

父组件应该存储活动子组件,就像您所做的那样,但是您应该将活动 ID 传递给子组件,以便它们“可点击”并允许父母更新活动孩子是什么。

OpenTaskAccordion

    修复构造函数中的this绑定 更新 handleClick 以使用任务 ID 来切换活动状态 将活动状态和onClick 回调传递给Task

代码

class OpenTaskAccordion extends Component 
  constructor() 
    super();
    this.state = 
      active: -1
    ;
    this.handleClick = this.handleClick.bind(this); // <-- fix this binding
  

  handleClick(id)  // <-- consume task id
    this.setState((prevState) => (
      ...prevState,
      active: id === prevState.active ? -1 : id 
    ));
  

  render() 
    const  active  = this.state;
    const  tasks = []  = this.props;

    return tasks.map((task) => (
      <OpenTask
        key=task.id
        active=active
        task=task
        onClick=this.handleClick
      />
    ));
  

打开任务

    删除所有 this 引用,因为这是一个功能组件。 消耗道具对象。 如果 props.active 与当前任务 ID 匹配,则添加“活动”类。 将props.onClick 附加到某物,可点击以切换父级中的活动状态。

代码

function OpenTask(props)  // <-- consume `props`!!
  return (
    <div
      id=props.task.id
      className=["tasks", props.active === props.task.id && "active"].join(" ")
      value=props.task.id
      onClick=() => props.onClick(props.task.id) // <-- attach onClick callback
    >
      <h1>props.task.clientName</h1>
      <div className="accordion-item accordion-item-closed">
        <h2>props.task.matter</h2>
        <p> - props.task.matterStatus</p>
      </div>
    </div>
  );

CSS 用于为活动项目切换应用“动画”。在背景颜色上使用简单的 CSS 过渡。

.tasks 
  transition: background-color ease-in-out 0.5s;


.active 
  background-color: lightblue;

评论问题

在功能性子组件中,我在哪里可以阅读有关此内容的信息 是在做? className=["tasks", props.active === props.task.id &amp;&amp; "active"].join(" ")

这只是一种创建空格分隔的类名列表的方法,即来自["tasks"]["tasks", "active"] 的“任务”或“活动任务”。

一些替代方法包括

className=`tasks $props.active === active ? "active" : ""`
className="tasks" + `$props.active === active ? " active" : ""`

在父类组件中,这是做什么的,为什么不 tasks =[] 被您在您的 例子?

const  active  = this.state;
const  tasks = []  = this.props;

const tasks = [] = this.props; 只是在this.props.tasks 未定义(或错误)的情况下为映射提供定义值的一种方式,就像tasks 道具是没有传递给组件。只要this.prop.tasks 是一个已定义的真值,那么这就是所使用的。将此视为后备值。

【讨论】:

感谢德鲁的详细回复。你所说的一切对我来说似乎都有意义(尽管我在问题中的措辞很糟糕)。作为后续,我想澄清一下,当我说“有没有办法针对孩子的“className”时,我的意思是,有没有办法针对目标孩子的 className 属性并更新它, 不是通过 className 来定位元素。换句话说,通过 id 来定位子元素,但将其 className 更改(在我的示例中,从“accordion-item-close”到“accordion-item-open”,反之亦然。再次感谢和抱歉措辞不佳。 @PanayiotisSpanos 啊,我明白了。是的,你可以这样做,但是 React 中的直接 DOM 操作是反模式的,你应该遵守 React 约定,例如传递一个 prop 并让子组件设置它的类。另一种方法是使用一个 react refs 数组(将 ref 传递给每个子元素以附加到一个元素)并在父元素中控制它,但是 IMO 比简单地传递一个道具并让孩子照顾更麻烦它。我更新了我的答案以反映这一澄清。 嘿德鲁,你能帮我澄清几件事吗?在功能性子组件中,我在哪里可以了解这是在做什么? "className=["tasks", props.active === props.task.id && "active"].join(" ")" 在父类组件中,这是做什么的,为什么不是" tasks =[ ] " 被您在示例中创建的数据集覆盖? " const active = this.state; const tasks = [ ] = this.props;" @PanayiotisSpanos 嗨,我尽力回答您的后续问题,尽我所能。您可以在我上面的答案末尾找到它们。如果我达到目标,请告诉我。

以上是关于React - 父类组件功能改变子组件的类的主要内容,如果未能解决你的问题,请参考以下文章

VUE组件的封装--父组件向子组件传值 调用父组件的方法改变子组件data&调用子组件方法

react 父子组件传值(兄弟传值)

react 子组件调用父组件方法

react 父子组件执行顺序

react最简单的子传父方案教学

react中父组件调用子组件方法(onRef)