如何在全局选项卡中创建动态元素

IT技术 javascript css reactjs react-hooks
2021-03-25 14:59:46

我正在使用 React 钩子,遇到了我现在卡住的问题。

我有一个 json 数据,每当用户获取时logs in,我都会创建一个顶部栏,user names当用户单击这些名称时,我将显示他们在 db 中为他们创建或呈现的一些数据,该数据我已经获取数据

我从服务器获取的数据

 let data=   [
  {
    "id": 1,
    "name": "Maxi",
    "myData": [
      {
        "data_name": "div1",
        "data_title": "div1 tittle"
      },
      {
        "data_name": "div1",
        "data_title": "div tittle"
      }
    ]
  },
  {
    "id": 2,
    "name": "Phill",
    "myData": [
      {
        "data_name": "div21",
        "data_title": "div21 tittle"
      }
    ]
  }
]

我有一个按钮,我想在上面创建新的 div,在我从顶部选择的名称内。

所以假设我已经列出topbar并且总共有 3 个数据,即 3 个名称,所以如果数据长度小于我正在做的 4,显示一个加号按钮来创建一个更多的选项卡,所以用户可以再创建一个选项卡,然后他们可以在每个选项卡中创建一个 div,

我做了什么

我正在填充顶部栏以及当我单击侧边按钮时,我正在div所选选项卡内创建一个

我面临的问题

  • 如果数据长度小于四,我将提供一个加号按钮以在更多选项卡上创建(给它默认名称),但我不知道如何创建新的。
  • 当我单击侧边按钮在选项卡内创建一次潜水,然后转到其他选项卡并再次返回时,我希望创建的内容div不应被删除。
  • 假设我有 3 个选项卡(数据),因此当页面加载我想要显示的第一个选项卡数据时,但它没有显示,我需要单击那里只显示它。
  • 如果数据为空,那么我最多可以创建 4 个选项卡,这也是我想要一些想法来做到这一点。

我在这里链接了我的代码沙盒,所以很容易理解我做了什么以及我想要做什么

代码沙箱

在代码沙箱中,我已经编写了所有代码,请查看。

我已经解释了我想要做的每一件事,我知道如何编码但没有任何想法或方法。

如果用户没有数据,那么我将开始为该用户从 1 创建选项卡和嵌套元素。

在每个创建的新元素中,都会有一个保存按钮,所以我会这样做,我唯一的顾虑是在创建它们后,当我将一个选项卡转到另一个选项卡时不要丢失 ui 上的数据

export default function App() {
  const [active_menu, setactive_menu] = useState(0);
  const [nestedData, setnestedData] = useState(null);

  let data1 = [
    {
      id: 1,
      name: "Maxi",
      myData: [
        {
          data_name: "div1",
          data_title: "div1 tittle"
        },
        {
          data_name: "div1",
          data_title: "div tittle"
        }
      ]
    },
    {
      id: 2,
      name: "Phill",
      myData: [
        {
          data_name: "div21",
          data_title: "div21 tittle"
        }
      ]
    }
  ];
  const Tab_click = (index, li) => {
    setactive_menu(index);
    setnestedData(li);
  };

  const Create_element = () => {
    //here I don't know how to create new elements
  };
  const addnewTab=()=>{
    
  }
  return (
    <div className="App row">
      {data1.map((li, index) => (
        <div className="col-4 col-sm-4 col-md-3 col-lg-3 col-xl-3" key={index}>
          <div
            className={
              index === active_menu
                ? "row dashboard_single_cont_active"
                : "row dashboard_single_cont"
            }
            onClick={() => Tab_click(index, li)}
          >
            <div className="dashboard_name col-10 col-sm-10 col-md-9 col-lg-10 col-xl-10">
              {li.name}
            </div>
            <div
              className={
                active_menu === index
                  ? "dashboard_option_active col-2 col-sm-2 col-md-3 col-lg-2 col-xl-2"
                  : "dashboard_option col-2 col-sm-2 col-md-3 col-lg-2 col-xl-2"
              }
              align="center"
            ></div>
          </div>
        </div>
      ))}
      {data1.length < 4 && (
       <span
       onClick={addnewTab}
       >ADD</span>
      )}
      <div className="col-11 col-sm-11 col-md-11 col-lg-11 col-xl-11">
        <div className="row">
          {nestedData !== null &&
            nestedData.myData.map((li, index) => (
              <div className="col-11 col-sm-11 col-md-8 col-lg-6 col-xl-6">
                <NEsted data={li} />
                <br></br>
              </div>
            ))}
        </div>
      </div>
      <RightBar Create_element={Create_element} />
    </div>
  );
}
1个回答

您面临的问题

  1. 如果数据长度小于四,我将提供一个加号按钮以在更多选项卡上创建(给它默认名称),但我不知道如何创建新的。

在这里你想获得一个新名称,即页面上的一个带有文本输入的表单,随机生成名称,使用提示,就像我在下面所做的那样。调用setData并使用功能状态更新将现有数据传播到数组中并附加新数据对象。

const addNewTab = () => {
  /**
   * window.prompt is just a quick way to request input from user. 
   */
  const name = window.prompt('Enter Name');

  /**
   * logic to generate next id, but could be anything really
   */
  const id = generateId();

  if (name) {
    setData((data) => [...data, new Data(id, name)]);
  }
};
  1. 当我点击侧边按钮在选项卡内创建一个潜水,然后我转到另一个选项卡并再次返回时,我希望创建的div不应该被删除。

这里发生了一些事情。首先,您data1在函数体中定义了它,以便在每个渲染周期重置它保持数据处于状态,data可以作为初始状态提供。其次,您需要添加创建的元素。通过更新状态来做到这一点。

移动data1出组件并定义与初始化的局部组件的状态data更新来自渲染的所有引用datadata)。

const [data, setData] = useState(data1);

类似于添加新选项卡的方法,获取新数据名称和标题。这里我们调用setData并再次使用功能状态更新,但这次我们将现有状态映射到一个新数组。当数组索引与活动选项卡索引匹配时,这就是我们需要更新的数据对象。将其展开myData到一个新数组中并附加新的数据对象。如果 index 不匹配,则只返回元素。

const Create_element = () => {
  /**
   * window.prompt is just a quick way to request input from user. 
   */
  const data_name = window.prompt('Enter Data Name');
  const data_title = window.prompt('Enter Data Title');

  if (data_name && data_title) {
    setData(data => data.map((el, i) => i === active_menu ? {
      ...el,
      myData: [...el.myData, { data_name, data_title }],
    } : el))
  }
};
  1. 假设我有 3 个选项卡(数据),因此当页面加载我想要显示的第一个选项卡数据时,但它没有显示,我需要单击那里只显示它。

这里的问题是由保持一个单独的nestedData状态对象引起的,该对象仅在单击选项卡时才更新。将新元素添加到活动选项卡的myData数组时nestedData不会更新。这里的解决方案是直接在 UI 中简单地呈现嵌套数据。

更新选项卡的 onClick 处理程序以简单地设置活动菜单(索引)。

onClick={() => setactive_menu(index)}

myData直接从新data状态渲染

data[active_menu].myData.map((li, index) => (...
  1. 如果数据为空,那么我最多可以创建 4 个选项卡,这也是我想要一些想法来做到的。

有条件地呈现 ADD 按钮将控制这一点。

{data.length < 4 && <button onClick={addNewTab}>ADD</button>}

编辑 how-to-create-dynamic-elements-inside-global-tabs

完整代码

import React, { useState } from "react";
import "./styles.css";

import RightBar from "./Right_option";
import NEsted from "./Nested";

import "bootstrap/dist/css/bootstrap.min.css";

/**
 * https://stackoverflow.com/questions/63814645/how-to-create-dynamic-elements-inside-global-tabs
 */

const data1 = [
  {
    id: 1,
    name: "Maxi",
    myData: [
      {
        data_name: "div1",
        data_title: "div1 tittle"
      },
      {
        data_name: "div1",
        data_title: "div tittle"
      }
    ]
  },
  {
    id: 2,
    name: "Phill",
    myData: [
      {
        data_name: "div21",
        data_title: "div21 tittle"
      }
    ]
  }
];

/**
 * Data object constructor
 * @param {number} id new data object id
 * @param {string} name new data object name
 * @example
 * new Data(1, 'Bill')
 */
const Data = (id, name) => ({ id, name, myData: [] });

/**
 * Id generator hook
 * @param {number} [seed=0] initial id generator value
 * @returns {function}
 */
const useIdGenerator = (seed = 0) => {
  function* genId(seed = 0) {
    let i = seed;
    while (true) yield i++;
  }

  const generator = genId(seed);

  return () => generator.next().value;
};

export default function App() {
  const generateId = useIdGenerator(5);

  const [data, setData] = useState(data1);
  const [active_menu, setactive_menu] = useState(0);

  const Create_element = () => {
    /**
     * window.prompt is just a quick way to request input from user.
     */
    const data_name = window.prompt("Enter Data Name");
    const data_title = window.prompt("Enter Data Title");

    if (data_name && data_title) {
      setData((data) =>
        data.map((el, i) =>
          i === active_menu
            ? {
                ...el,
                myData: [...el.myData, { data_name, data_title }]
              }
            : el
        )
      );
    }
  };

  const addNewTab = () => {
    /**
     * window.prompt is just a quick way to request input from user.
     */
    const name = window.prompt("Enter Name");

    if (name) {
      setData((data) => [...data, new Data(generateId(), name)]);
    }
  };

  return (
    <div className="App row">
      {data.map((li, index) => (
        <div className="col-4 col-sm-4 col-md-3 col-lg-3 col-xl-3" key={index}>
          <div
            className={
              index === active_menu
                ? "row dashboard_single_cont_active"
                : "row dashboard_single_cont"
            }
            onClick={() => setactive_menu(index)}
          >
            <div className="dashboard_name col-10 col-sm-10 col-md-9 col-lg-10 col-xl-10">
              {li.name}
            </div>
            <div
              className={
                active_menu === index
                  ? "dashboard_option_active col-2 col-sm-2 col-md-3 col-lg-2 col-xl-2"
                  : "dashboard_option col-2 col-sm-2 col-md-3 col-lg-2 col-xl-2"
              }
              align="center"
            ></div>
          </div>
        </div>
      ))}

      {data.length < 4 && <button onClick={addNewTab}>ADD</button>}

      <div className="col-11 col-sm-11 col-md-11 col-lg-11 col-xl-11">
        <div className="row">
          {data[active_menu].myData.map((li, index) => (
            <div
              key={index}
              className="col-11 col-sm-11 col-md-8 col-lg-6 col-xl-6"
            >
              <NEsted data={li} />
              <br></br>
            </div>
          ))}
        </div>
      </div>
      <RightBar Create_element={Create_element} />
    </div>
  );
}
@manishthakur 你确定吗?我仍然在选项卡中打开了沙箱,并且无法重现任何双窗口提示(“创建元素”有两个提示,一个是名称,一个是标题)。此外,正如评论所说,这只是获取用户输入的快速技巧;您可能希望使用表单和文本输入来获得更好的用户体验。
2021-06-12 14:59:46
无论如何,我不会要求用户输入名称,最初我只想创建一个名称,如破折号 1、破折号 2,我可以相应地操作您的代码吗?正确的 ?
2021-06-13 14:59:46
@manishthakur codesandbox.io/s/...添加deleteTab处理程序,添加按钮,并使用可选进行空处理data[active_menu]?.myData.map
2021-06-13 14:59:46
我检查了你的代码,发现当我点击添加或创建元素时提示会显示两次
2021-06-17 14:59:46
@manishthakur 您需要将数据映射到一个新数组,当映射索引等于 active_menu 时,将该元素浅复制到一个新对象中,然后您可以myData按索引进行过滤。我更新了我链接的最后一个代码和框。codeandbox.io/s/…
2021-06-18 14:59:46