遍历嵌套对象

时间:2019-10-05 10:47:13

标签: javascript arrays reactjs object

我正在创建一个React应用,并且具有以下列表:

const list = [
    {
        id: '1',
        task: 'task 1',
        activities: [{
            'Google': [
                {url: 'https://www.google.com'},
                {visited: false}
            ],
            'Yahoo': [
                {url: 'https://www.yahoo.com'},
                {visited: false}
            ],
            'Bing': [
                {url: 'https://www.bing.com'},
                {visited: false}
            ]
        }],
        visitedAll: false
    },
    {
        id: '2',
        task: 'task 2',
        activities: [{
            'Facebook': [
                {url: 'https://www.facebook.com'},
                {visited: false}
            ],
            'Instagram': [
                {url: 'https://www.instagram.com'},
                {visited: false}
            ],
            'Twitter': [
                {url: 'https://www.twitter.com'},
                {visited: false}
            ]
        }],
        visitedAll: false
    }
];

我正在遍历列表,如下所示:

<ul>
    {list.map(item => (
        <li key={item.id}>
           {item.task}
           <ul>
               {Object.keys(item.activities[0]).map((act, i) => <li key={i}>{act}</li>)}
           </ul>
        </li>
    ))}
</ul>

这将产生以下输出:

        
  •         任务1         
                  
    • Google
    •             
    • 雅虎
    •             
    • Bing
    •         
        
  •     
  •         任务2         
                  
    • Facebook
    •             
    • Instagram
    •             
    • Twitter
    •         
        
  1. 如何将子菜单列表项及其对应的锚点包裹起来?
  2. 如何添加onClick事件,以将每个项目的visited键设置为true

3 个答案:

答案 0 :(得分:0)

您可以先使用activities方法将reduce属性修改为一个对象,然后为每个带有visited属性的状态子项目创建单独的组件。

const {useState} = React;

const list = [{"id":"1","task":"task 1","activities":[{"Google":[{"url":"https://www.google.com"},{"visited":false}],"Yahoo":[{"url":"https://www.yahoo.com"},{"visited":false}],"Bing":[{"url":"https://www.bing.com"},{"visited":false}]}],"visitedAll":false},{"id":"2","task":"task 2","activities":[{"Facebook":[{"url":"https://www.facebook.com"},{"visited":false}],"Instagram":[{"url":"https://www.instagram.com"},{"visited":false}],"Twitter":[{"url":"https://www.twitter.com"},{"visited":false}]}],"visitedAll":false}]
.map(({activities, ...rest}) => ({
  ...rest, activities: activities.reduce((r, e) => Object.assign(r, e), {})
}))

const SubListItem = props => {
  const {data, name} = props;
  const [visited, setVisited] = useState(data[1].visited);
  
  const toggle = event => {
    event.preventDefault();
    setVisited(!visited);
  }
  
  return <li>
    <a style={{color: visited ? "green" : ''}}
    onClick={toggle} 
    href={data[0].url}>{name}</a>
  </li>
}

const List = ({data}) => <ul>
  {data.map(item => <li key={item.id}>
    <a href="">{item.task}</a>
    <ul>{Object.keys(item.activities).map(act => (
      <SubListItem key={act} name={act} data={item.activities[act]} />
    ))}</ul>
  </li>)}
</ul>

ReactDOM.render(<List data={list} />, document.querySelector("#app"))
body {
  background: #20262E;
  padding: 20px;
  font-family: Helvetica;
}

#app {
  background: #fff;
  border-radius: 4px;
  padding: 20px;
  transition: all 0.2s;
}

li {
  margin: 8px 0;
}
<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="app"></div>

答案 1 :(得分:0)

我将实现一个Anchor组件,该组件将保存visited的状态。

此外,父组件App的状态为“所有访问的锚”。

Example:

const list = [
  {
    id: '1',
    task: 'task 1',
    activities: {
      Google: {
        url: 'https://www.google.com'
      },
      Yahoo: {
        url: 'https://www.yahoo.com'
      },
      Bing: {
        url: 'https://www.bing.com'
      }
    }
  },
  {
    id: '2',
    task: 'task 2',
    activities: {
      Facebook: {
        url: 'https://www.facebook.com'
      },
      Instagram: {
        url: 'https://www.instagram.com'
      },
      Twitter: {
        url: 'https://www.twitter.com'
      }
    }
  }
];

const Anchor = ({ name, href, onClick }) => {
  const [isVisited, setIsVisited] = useState(false);
  const onAnchorClick = () => {
    setIsVisited(visited => !visited);
    onClick();
  };
  return (
    // v add href={href}
    <a onClick={onAnchorClick}>{`${name} is ${
      isVisited ? '' : 'not'
    } visited`}</a>
  );
};

// You can get such object from reducing the list
const visitedInitial = {
  Google: false,
  Yahoo: false,
  Bing: false,
  Facebook: false,
  Instagram: false,
  Twitter: false
};

const App = () => {
  const [visited, setVisited] = useState(visitedInitial);

  return (
    <>
      <h1>
        {Object.values(visited).every(value => value === true)
          ? 'All Visited'
          : 'Please visit all'}
      </h1>
      <ul>
        {list.map(({ id, task, activities }) => (
          <li key={id}>
            {task}
            <ul>
              {Object.entries(activities).map(([name, { url }], i) => (
                <li key={i}>
                  <Anchor
                    name={name}
                    href={url}
                    target="_blank"
                    onClick={() =>
                      setVisited(prev => ({ ...prev, [name]: true }))
                    }
                  />
                </li>
              ))}
            </ul>
          </li>
        ))}
      </ul>
    </>
  );
};

Edit Q-58247570-UL

答案 2 :(得分:0)

这是根据您提供的项目提供的答案。我已经将绘图子项目提取到一个接受项目参数的单独函数中。每个子项目都有一个onClick处理程序,该处理程序将visited设置为true

import React, { useState, useCallback } from "react";
import ReactDOM from "react-dom";

const initialItems = [
  {
    id: "1",
    task: "task 1",
    activities: [
      {
        Google: [{ url: "https://www.google.com" }, { visited: false }],
        Yahoo: [{ url: "https://www.yahoo.com" }, { visited: false }],
        Bing: [{ url: "https://www.bing.com" }, { visited: false }]
      }
    ]
  },
  {
    id: "2",
    task: "task 2",
    activities: [
      {
        Facebook: [{ url: "https://www.facebook.com" }, { visited: false }],
        Instagram: [{ url: "https://www.instagram.com" }, { visited: false }],
        Twitter: [{ url: "https://www.twitter.com" }, { visited: false }]
      }
    ]
  }
];

function App() {
  const [items, setItems] = useState(initialItems);

  const setVisited = React.useCallback(
    (id, activityName) => {
      const item = items.find(item => item.id === id);

      if (item) {
        item.activities[0][activityName][1].visited = true;
        setItems([...items]);
      }
    },
    [items, setItems]
  );

  const getSubItems = useCallback(
    item => {
      const activities = item.activities[0];

      return Object.keys(activities).map(activityName => {
        const url = activities[activityName][0].url;
        const onClick = () => {
          setVisited(item.id, activityName);
        };

        return (
          <li key={activityName}>
            <a
              href={url}
              onClick={onClick}
              target="_blank"
              rel="noopener noreferrer"
            >
              {activityName}
            </a>
          </li>
        );
      });
    },
    [setVisited]
  );

  return (
    <div>
      <ul>
        {items.map(item => (
          <li key={item.id}>
            {item.task}
            <ul>{getSubItems(item)}</ul>
          </li>
        ))}
      </ul>
    </div>
  );
}

const rootElement = document.getElementById("root");
ReactDOM.render(<App />, rootElement);

EXAMPLE

相关问题