React Material UI Multiple Collapse

В настоящее время все мои списки свернуты, но они связаны с одним состоянием "открыто", поэтому, если я открываю один список, все остальные списки открываются. Что является лучшим способом сохранить коллапс отдельно друг от друга, не имея большого количества состояний для каждого списка.

РЕДАКТИРОВАТЬ: приложение проходит бесконечный цикл

App.tsx

interface IState {
error: any,
intro: any,
threads: any[],
title: any,
}

export default class App extends React.Component<{}, IState> {
    constructor (props : any) {
        super (props);

        this.state = {
            error: "",
            intro: "Welcome to RedQuick",
            threads: [],
            title: ""
        };

        this.getRedditPost = this.getRedditPost.bind(this)
        this.handleClick = this.handleClick.bind(this)
    }

    public getRedditPost = async (e : any) => {
        e.preventDefault();

        const subreddit = e.target.elements.subreddit.value;
        const redditAPI = await fetch('https://www.reddit.com/r/'+ subreddit +'.json');
        const data = await redditAPI.json();

        console.log(data);

        if (data.kind) {
            this.setState({
                error: undefined,
                intro: undefined,
                threads: data.data.children,
                title: data.data.children[0].data.subreddit.toUpperCase()
            });
        } else {
            this.setState({
                error: "Please enter a valid subreddit name",
                intro: undefined,
                threads: [],
                title: undefined
            });
        }
    }

    public handleClick = (index : any)  => {
        this.setState({ [index]: true });
    }

    public render() {
        return (
            <div>
                <Header 
                    getRedditPost={this.getRedditPost}
                />
                <p className="app__intro">{this.state.intro}</p>
                {
                    this.state.error === "" && this.state.title.length > 0 ?
                    <LinearProgress />:
                    <ThreadList 
                        error={this.state.error}
                        handleClick={this.handleClick}
                        threads={this.state.threads}
                        title={this.state.title}
                    />
                }   
            </div>
        );
    }
}

Threadlist.tsx

<div className="threadlist__subreddit_threadlist">
    <List>
        { props.threads.map((thread : any, index : any) => 
            <div key={index} className="threadlist__subreddit_thread">
                <Divider />
                <ListItem button={true} onClick={props.handleClick(index)}/* component="a" href={thread.data.url}*/ >
                    <ListItemText primary={thread.data.title} secondary={<p><b>Author: </b>{thread.data.author}</p>} />
                    {props[index] ? <ExpandLess /> : <ExpandMore />}
                </ListItem>
                <Collapse in={props[index]} timeout="auto" unmountOnExit={true}>
                    <p>POOP</p>
                </Collapse>
                <Divider />
            </div>
        ) }
    </List> 
</div>

4 ответа

Вы должны создавать разные состояния для каждого свертывания, я предлагаю динамически использовать setState с индексом, который вы получили от функции map, вам, вероятно, придется передать параметр index в функцию handleClick и изменить состояние на основе этого.

<div className="threadlist__subreddit_threadlist">
    <List>
        { props.threads.map((thread : any, index : any) => 
            <div key={index} className="threadlist__subreddit_thread">
                <Divider />
                <ListItem button={true} onClick={props.handleClick(index)}/* component="a" href={thread.data.url}*/ >
                    <ListItemText primary={thread.data.title} secondary={<p><b>Author: </b>{thread.data.author}</p>} />
                    {props[index] ? <ExpandLess /> : <ExpandMore />}
                </ListItem>
                <Collapse in={props[index]} timeout="auto" unmountOnExit={true}>
                    <p>POOP</p>
                </Collapse>
                <Divider />
            </div>
        ) }
    </List> 
</div>

Ваш handleClick должен выглядеть примерно так: publi

public handleClick = (index : any)  => {
        this.setState({ [index]: true });
    }

Я сделал это так:

function DrawerMenuItems() {
  const [selectedIndex, setSelectedIndex] = React.useState("")

  const handleClick = index => {
    if (selectedIndex === index) {
      setSelectedIndex("")
    } else {
      setSelectedIndex(index)
    }
  }
  return (
    <List
      component="nav"
      aria-labelledby="nested-list-subheader"
      subheader={
        <ListSubheader component="div" id="nested-list-subheader">
          Nested List Items
        </ListSubheader>
      }
    >
      {drawerMenuItemData.map((item, index) => {
        return (
          <List>
            <ListItem
              key={index}
              button
              onClick={() => {
                handleClick(index)
              }}
            >
              <ListItemIcon>
                <item.icon />
              </ListItemIcon>
              <ListItemText primary={item.title} />
              {index === selectedIndex ? <ExpandLess /> : <ExpandMore />}
            </ListItem>
            <Collapse in={index === selectedIndex} timeout="auto" unmountOnExit>
              <List component="div" disablePadding>
                {item.submenu.map((sub, index) => {
                  return (
                    <ListItem button >
                      <ListItemText primary={sub.name} />
                    </ListItem>
                  )
                })}
              </List>
            </Collapse>
          </List>
        )
      })}
    </List>
  )
}

вы также можете использовать компонент коллапса дизайна муравьев. Я сделал из него вложенный компонент коллапса. в основном, использование библиотеки не имеет значения. Важно то, как вы передаете данные и как вы контролируете активный ключ. Вот мой пример ...

      const ProductCatagoryHierarchy = () => {

 const [tree, setTree] = useState([])

 useEffect( async () => {

    const arr2 = [
            {id: 1, name: 'gender', parent: null, parent_id: null },
            {id: 2, name: 'material', parent: null, parent_id: null},
            { id: 3, name: 'male', parent: 1, parent_name: "gender" },
            { id: 5, name: 'female', parent: 1, parent_name: "gender" },
            { id: 4, name: 'shoe', parent: 3, parent_id: "male" },
        ]

        let newarr=[];
        for(let i=0 ; i< arr2.length; i++ ){

            if(arr2[i].id){
                if(newarr[i] != {} ){
                    newarr[i] = {}
                }
                newarr[i].id = arr2[i].id 
            }
            if( arr2[i].name ){
                newarr[i].name = arr2[i].name 
            }
            if( arr2[i].parent ){
                newarr[i].parent = arr2[i].parent 
            }
            if( arr2[i].parent_id ){
                newarr[i].parent_id = arr2[i].parent_id 
            }

            newarr[i].products = arr2[i].products;
        }

        console.log('newarr', newarr );

        let tree = function (data, root) {
            var t = {};
            data.forEach(o => {
                Object.assign(t[o.id] = t[o.id] || {}, o);
                t[o.parent] = t[o.parent] || {};
                t[o.parent].children = t[o.parent].children || [];
                t[o.parent].children.push(t[o.id]);
            });
            return t[root].children;
        }(newarr, undefined);
        
        console.log('tree ', tree);
        setTree(tree)
 }, [])

  const childFunc = (children) => {
        // console.log('children', children );
        let childPanel = children.map((child, i) => {
            return(
                <Collapse /* defaultActiveKey={child.id} */  
                style={{ margin: '10px 0' }}  key={i} 
                expandIcon={({ isActive }) => 
               <CaretRightOutlined rotate={isActive ? 90 : 0} />}
                >
                    <Panel header={child.name} key={child.id}   >
                    <p>  {child?.products?.length > 0 ? 
                    <p> Products: <strong> 
                  { child.products.map(x =>  x.name  ).join(', ') } 
                  </strong> </p> : 'No Product Available'} </p> 

                    {child?.children?.length > 0 ? 
                    childFunc(child.children) : null }
                    </Panel>
                </Collapse>
            )
        })

        return childPanel; 
    }

    return(
        <div>

        <h1> Category Hierarchy </h1>

        {/* <Button onClick={() => onclick()} > test </Button> */}


        <Collapse  >
            {
                tree.map((x,i,l) => {
                console.log('x,i,l', x, i, l );
                return(
                <Panel header={x.name} key={x.id}  
               style={{ backgroundColor: 'darkgray' }} >
                <p>  {x?.products?.length > 0 ? 
               <p> Products: <strong> 
               { x.products.map(x => x.name).join(', ') } 
               </strong> </p> : 'No Product Available'} </p> 
                {
                    x?.children?.length > 0 ? 
                    childFunc(x.children)
                    :
                    null
                }
                </Panel>
                )
                })
            }

            
        </Collapse>

        </div>
    )
}

export default ProductCatagoryHierarchy;

Вот как я это сделал

      interface IObjectKeys {
[key: number]: boolean;

}

      const [getCollapse, setCollapse] = React.useState<IObjectKeys>({
    0: false,
    1: false,
    2: false,
    3: false,
    4: false,
    5: false,
    6: false
});



const handleCollapse = (target: any)  => {
    setCollapse({ [target]: !getCollapse[target] });
}

И вы можете использовать вот так

      <Collapse in={getCollapse[1]} timeout="auto" unmountOnExit>
                            <ListItem>
                                <Typography>Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Convallis convallis tellus id interdum velit</Typography>
                            </ListItem>
                        </Collapse>

<Collapse in={getCollapse[2]} timeout="auto" unmountOnExit>
                            <ListItem>
                                <Typography>Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Convallis convallis tellus id interdum velit</Typography>
                            </ListItem>
                        </Collapse>
Другие вопросы по тегам