React组件嵌套父组件与子组件之间的信息传递


我使用react18.2 实现了list集合嵌套树形组件的操作,
我新建的一个集合,会同时生成集合里面的树形组件,

集合树形组件数据结构:
集合1
   树形节点 <标签>
 
集合2 
   树形节点  <标签>
我在传递到数据到子组件,点击父组件中的对应的标签进行展示的时候,无论点击那个集合里面的树形组件标签都显示是最后一个集合中树形组件的数据

例如:
我新建立了两个集合
集合1 树形控件数据 01 方案 01 节点 01属性
我再建立一个集合 02 方案 02 节点 02 属性
集合2新建完成后点击标签查看属性,看集合1的属性会展示集合2的属性 每次都会把最开始新建的给覆盖掉
以下是部分的代码

父组件中
import { Avatar, List,Collapse,TreeSelect,Modal   } from 'antd';
import React, { useState,useRef } from 'react';
import { PlusCircleOutlined,SolutionOutlined,DeleteOutlined,SearchOutlined} from '@ant-design/icons';
保存数据的方法

const [treeData, setTreeData] = useState([]);// 初始化树形数据
  const [selectedKey,setSelectedKey]=useState(null);
  const [basicData, setBasicData] = useState(null);
  const [isModalOpenMeta,setIsModalOpenMeta]=useState(false);
  const [basicMeta,setBasicMeta]=useState(null);
  const [parentnode,setparentnode]=useState(null);
  const [treeDataMap, setTreeDataMap] = useState({}); // 添加 treeDataMap 状态
  const [refreshFlag, setRefreshFlag] = useState(0); // 定义一个 refreshFlag 状态
 

 // 在父组件中定义一个 useRef 变量来保存属性数据对象
const attributeDataRef = useRef({});

        const handleSave = (formData, isTree) => {
          debugger
          const TreeNode={
            title: (
              <span>
                {`${formData.newNodeName}`}
                <SolutionOutlined  onClick={() => showModalBaisc(attributeDataRef.current)} style={{ marginLeft: '5px', marginRight: '5px' }}/> 
                <PlusCircleOutlined onClick={showModalForm} style={{ marginLeft: '5px', marginRight: '5px' }} /> 
              </span>
            ),
            value: randomId,
            children: []
          };

        if(isTree){
          const newNode = {
            title: (
              <span>
                {`${formData.newNodeNo}-${formData.newNodeName}`}
                <DeleteOutlined onClick={(event) => handleDelete(`${formData.newNodeNo}-${formData.newNodeName}`, event)} />
              </span>
            ),
            value: randomId,
          }; 
          // 更新映射表中的数据
          const newTreeDataMap = { ...treeDataMap };
          newTreeDataMap[TreeNode.value] = [TreeNode];  // 新增 newnode 子节点
          setTreeDataMap(newTreeDataMap);
          // 若不是树形节点,则直接将其添加到列表数据中
          setSelectedNode(newNode); // 将新节点设置为当前所选节点
          setListData([...listData, newNode]);
        }else{
            // 将新的树形节点附加到现有列表数据中
            setListData([...listData, TreeNode]);
        }
        //赋值
        debugger
        setparentnode(formData.Parentlevel);
        attributeDataRef.current = { ...attributeDataRef.current, ...formData };
        setSelectedAttributeData({ ...attributeDataRef.current });
        setRefreshFlag(refreshFlag + 1);
      };

const showModalBaisc = (data) => {
   setSelectedNode(data);
   setIsModalOpenBasic(true);
};

引用子组件
<SolutionComponets
visible={isModalOpenBaic} 
onClose={handleCloseModalBasic}
// AttributeDataRef={attributeDataRef}
attributeDataRef={{ ...attributeDataRef.current }} 
refreshFlag={refreshFlag}
/>

渲染组件

<List style={{width:'50%'}}
        itemLayout="horizontal"
        dataSource={listData}
        renderItem={(item, index) => (
        <Collapse bordered>
          <Panel header={item.title} key={index}>
            <List.Item.Meta
              avatar={<Avatar src={`https://xsgames.co/randomusers/avatar.php?g=pixel&key=${index}`} />}
              description="This is a solution project for generating Word documents"
          
          />
          {/* 在 TreeSelect 中通过 getTreeDataForListItem 函数获取子节点列表数据 */}
          <TreeSelect  {...tProps} onSelect={onSelect}  key={refreshFlag}  treeData={getTreeDataForListItem(item.value)}  />
          </Panel>
        </Collapse>

        )}
      />
子组件中

import { Drawer,Input } from 'antd';
import React, { useState, useEffect } from 'react';

function SolutionComponents({ visible, onClose, attributeDataRef,refreshFlag  }) {
  const [selectedAttributeData, setSelectedAttributeData] = useState({});
  
  useEffect(() => {
    // 当 refreshFlag 发生变化时,清空之前的属性数据,重新获取新的属性数据
    setSelectedAttributeData({});
    setTimeout(() => {
      setSelectedAttributeData({ ...attributeDataRef });
    }, 0);
  }, [attributeDataRef, refreshFlag]);

    const handleCloseDrawer = () => {
       // 在关闭组件前,将修改后的属性数据传递回父组件
    attributeDataRef.current = { ...attributeDataRef.current, ...selectedAttributeData };
   onClose();
   
 };



 return (
   <>
    <Drawer title="窗体属性" visible={visible} placement="right" onClose={handleCloseDrawer}>
      <p>{selectedAttributeData ? selectedAttributeData.newNodeNo : ''}</p>
      <p>{selectedAttributeData ? selectedAttributeData.newNodeName : ''}</p>
      <p>{selectedAttributeData ? selectedAttributeData.ProjecTtype : ''}</p>
      <p>{selectedAttributeData ? selectedAttributeData.Parentlevel : ''}</p>
    </Drawer>
   </>
 );

}

export default SolutionComponents;

img

img

img

我想看看你的onSelect函数的详情

看起来你的问题可能出在传递数据的方式上。你需要确保在传递数据时,每个树形组件都收到了它所需要的数据。现在的问题是,当你点击标签时,无论你点击哪个集合中的树形组件,都会显示最后一个集合的树形组件数据。

你可以尝试使用React的Context API来解决这个问题。Context API 是 React 提供的一种机制,用于在组件树中共享数据。你可以在最外层的组件中创建一个Context对象,并将你的集合数据存储在这个Context中。然后,你可以在树形组件中使用React的 useContext hook 来获取这个Context中的数据。这样,每个树形组件都可以独立地获取它所需要的数据。

以下是一个使用Context API 的例子:

import React, { createContext, useContext, useState } from "react";

// 创建一个Context对象
const CollectionContext = createContext();

// 最外层的组件
function App() {
  const [collections, setCollections] = useState([]);

  // 在这里创建一个函数,用于向集合中添加数据
  function addCollection(collection) {
    setCollections([...collections, collection]);
  }

  return (
    // 将集合数据存储在Context中
    <CollectionContext.Provider value={collections}>
      <div>
        <button onClick={() => addCollection({ name: "集合1", nodes: [] })}>
          添加集合1
        </button>
        <button onClick={() => addCollection({ name: "集合2", nodes: [] })}>
          添加集合2
        </button>
        <Tree />
      </div>
    </CollectionContext.Provider>
  );
}

// 树形组件
function Tree() {
  const collections = useContext(CollectionContext);

  // 这里可以根据需要获取集合数据
  console.log(collections);

  return (
    <div>
      <ul>
        {collections.map((collection) => (
          <li key={collection.name}>
            {collection.name}
            <ul>
              {collection.nodes.map((node) => (
                <li key={node.name}>{node.name}</li>
              ))}
            </ul>
          </li>
        ))}
      </ul>
    </div>
  );
}

在这个例子中,我们创建了一个名为CollectionContext的Context对象,并将集合数据存储在其中。在App组件中,我们通过使用CollectionContext.Provider将这个Context传递给了所有的子组件。在Tree组件中,我们使用useContext hook 来获取这个Context中的数据。这样,无论我们点击哪个集合中的树形组件,都会正确地显示对应的数据。

  • 这有个类似的问题, 你可以参考下: https://ask.csdn.net/questions/7462406
  • 这篇博客你也可以参考下:React子组件与父组件之间的通信
  • 除此之外, 这篇博客: React 函数式组件之父组件调用子组件的方法中的 第二步: 部分也许能够解决你的问题, 你可以仔细阅读以下内容或跳转源博客中阅读:
  • 子组件中代码:(使用了 forwardRefuseImperativeHandle

    import { forwardRef, useImperativeHandle } from 'react'
    const ChildCom = forwardRef(({
      // 这里是一些props参数
    }, ref) => {
        useImperativeHandle(ref, () => ({
            getData: getData,
            otherFun: otherFun
        }))
        function getData() {
            // to do something
        }
        function otherFun() {
            console.log('这是其他方法')
        }
        return (
            <div>子组件</div>
        )
    })
  • 您还可以看一下 刘忠彬老师的React极速入门指南课程中的 如何高效率的学习本系列课程小节, 巩固相关知识点
  • 以下回答由chatgpt基于相关博客总结生成:

    将需要传递的状态或方法通过props传递给子组件即可。具体步骤如下:

    1. 在父组件中定义需要传递的状态或方法。

    例如,若父组件中有一个状态为count,需要将其传递给子组件ChildComponent:

    class ParentComponent extends React.Component {
      constructor(props) {
        super(props);
        this.state = {
          count: 0
        };
      }
    
      // 定义修改count的方法
      incrementCount = () => {
        this.setState(prevState => ({
          count: prevState.count + 1
        }));
      }
    
      render() {
        return <ChildComponent count={this.state.count} incrementCount={this.incrementCount} />;
      }
    }
    
    1. 在子组件中通过props获取父组件传递的状态或方法。

    例如,在子组件中将父组件传递的count状态显示出来,并调用incrementCount方法:

    class ChildComponent extends React.Component {
      render() {
        return (
          <div>
            <p>Count: {this.props.count}</p>
            <button onClick={this.props.incrementCount}>Increment</button>
          </div>
        );
      }
    }
    

    这样,当点击子组件中的“Increment”按钮时,会调用父组件的incrementCount方法,从而修改父组件中的count状态。同时,父组件传递的count状态也会在子组件中显示出来。

    另外,如果需要在子组件中直接修改父组件的状态,可以通过向父组件传递一个回调函数实现。详见上面提到的官方文档中的“Lifting State Up”部分。

    需要注意的是,在传递props时,为了防止子组件修改父组件传递的状态,应该将传递的对象或数组进行浅拷贝后再传递。