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>