Reputation: 3520
I have a working prototype where I cant seem to figure out when I click on a particular button in my nav, then pass that index to another component. It doesn't update the index when I query using "document.querySelector". My goal is to just move the content to the front child position/firstChild depending on what button you clicked. So if you clicked the first button then the last, my last content would be first and so and so on...I've tried a timeout and still no luck.
JS:
.....
const Context = createContext(false);
const dataArr = [
{
id: "113062",
name: "Blue",
region: "US",
sort: "1"
},
{
id: "115102",
name: "Red",
region: "DE",
sort: "2"
},
{
id: "70884",
name: "Green",
region: "US",
sort: "3"
},
{
id: "114683",
name: "Yellow",
region: "US",
sort: "4"
},
{
id: "112878",
name: "Pale",
region: "US",
sort: "5"
},
{
id: "114682",
name: "Orange",
region: "US",
sort: "6"
},
{
id: "120093",
name: "Black",
region: "CH",
sort: "8"
},
{
id: "120594",
name: "White",
region: "CH",
sort: "9"
}
];
const useStyles = makeStyles((theme) => ({
root: {
display: "flex",
"& > *": {
margin: theme.spacing(1)
}
},
grey: {
border: "4px solid white"
},
orange: {
color: theme.palette.getContrastText(deepOrange[500]),
backgroundColor: deepOrange[500],
border: "4px solid black"
},
info: {
margin: "10px"
},
wrapper: {
display: "flex"
},
contentWrapper: {
display: "flex",
flexDirection: "column"
},
elWrapper: {
opacity: 0,
"&.active": {
opacity: 1
}
}
}));
const ToggleItem = ({ id, description }) => {
const { handleChange, selected, classes } = useContext(Context);
const isSelected = selected.includes(description);
const handleClick = (idx) => {
console.log("idx====", idx);
handleChange(description, idx);
};
return (
<>
<Avatar
className={isSelected ? classes.orange : classes.grey}
onClick={() => handleClick(id)}
>
<span style={{ fontSize: ".75rem" }}>{description}</span>
</Avatar>
</>
);
};
const ToggleContainer = ({ selected, list }) => {
const isSelected = list.filter((element) => selected.includes(element));
return (
<>
{dataArr.map((item, idx) => (
<div
key={idx}
className="carouselWrapper"
style={{ display: isSelected.includes(item.name) ? "block" : "none" }}
>
{item.name}
</div>
))}
</>
);
};
const ToggleWrapper = () => {
const data = [];
dataArr.map((el) => data.push(el.name));
const classes = useStyles();
const [selected, setSelected] = useState([]);
const [viewAll, setViewAll] = useState(true);
const handleChange = (val, idx) => {
setViewAll(false);
setSelected((selected) => {
if (selected.length === 1 && selected.includes(val)) {
return handleViewAll();
}
if (selected.includes(val)) {
return selected.filter((v) => v !== val);
} else {
return [val, ...selected];
}
});
setTimeout(() => {
const someParentObject = document.getElementById("contentWrapper");
const someChildObject = document.querySelectorAll(".carouselWrapper")[
idx
];
// eslint-disable-next-line no-unused-expressions
selected.length > 0 &&
someParentObject.insertBefore(
someChildObject,
someParentObject.firstChild
);
console.log(
"someParentObject.children[0]==",
someParentObject.firstChild
);
console.log("someChildObject", someChildObject);
}, 2000);
};
const handleViewAll = () => {
setViewAll(true);
setSelected([]);
};
return (
<Context.Provider
value={{
viewAll,
handleChange,
handleViewAll,
selected,
classes
}}
>
<div className={classes.wrapper}>
<Avatar
className={viewAll ? classes.orange : null}
onClick={handleViewAll}
>
<span style={{ fontSize: "0.75rem", textAlign: "center" }}>
View All
</span>
</Avatar>
{dataArr.map((d, id) => {
return (
<div key={id}>
<ToggleItem id={id} description={d.name} />
</div>
);
})}
</div>
<div id="contentWrapper" className={classes.contentWrapper}>
<ToggleContainer selected={viewAll ? data : selected} list={data} />
</div>
</Context.Provider>
);
};
export default function App() {
return <ToggleWrapper />;
}
Upvotes: 0
Views: 391
Reputation: 12952
Here is a very quick example of one option. It stores the data
in a Map
which allows for efficient retrieval (this could be moved to context to avoid passing it to multiple child components) and tracks selected
by id
in a separate state array. It's then just a matter of mapping the selected
array to get your ordered list. If selected
is empty render the full values()
of the Map.
const { useState, useEffect } = React;
function App({ data }) {
const [selected, setSelected] = useState([]);
const [itemMap, setItemMap] = useState(new Map());
useEffect(() => {
setItemMap(new Map(data.map(({ id, ...rest }) => [id, { id, ...rest }])));
}, [data]);
const selectHandler = (id) => {
setSelected(selected => {
if (id === undefined) return [];
return selected.includes(id)
? selected.filter(_id => _id !== id)
: [id, ...selected];
});
}
return (
<div>
<Header itemMap={itemMap} selected={selected} selectHandler={selectHandler} />
<Selected itemMap={itemMap} selected={selected} />
</div>
);
}
function Header({ itemMap, selected, selectHandler }) {
return (
<div className={"header-container"}>
<div className={!selected.length && 'selected'} onClick={() => selectHandler()}>All</div>
{[...itemMap.values()].map(({ id, name }) => (
<div key={id} className={selected.includes(id) && 'selected'} onClick={() => selectHandler(id)}>
{name}
</div>
))}
</div>
);
}
function Selected({ itemMap, selected }) {
return (
<div className={"selected-container"}>
{selected.length
? selected.map((id) => (
<div key={id} >
{itemMap.get(id).name}
</div>
))
: [...itemMap.values()].map(({ id, name }) => (
<div key={id} >
{name}
</div>
))
}
</div>
);
}
const dataArr = [{ id: "113062", name: "Blue", region: "US", sort: "1" }, { id: "115102", name: "Red", region: "DE", sort: "2" }, { id: "70884", name: "Green", region: "US", sort: "3" }, { id: "114683", name: "Yellow", region: "US", sort: "4" }, { id: "112878", name: "Pale", region: "US", sort: "5" }, { id: "114682", name: "Orange", region: "US", sort: "6" }, { id: "120093", name: "Black", region: "CH", sort: "8" }, { id: "120594", name: "White", region: "CH", sort: "9" }];
const container = document.getElementById('root');
const root = ReactDOM.createRoot(container);
root.render(<App data={dataArr} />);
.header-container { width: 100vw; display: flex; justify-content: flex-start;}
.header-container div { width: 40px; height: 40px; display: flex; overflow: hidden; position: relative; margin: 4px; font-size:.75rem; align-items: center; user-select: none; border-radius: 50%; justify-content: center; color: #fafafa; background-color: #bdbdbd;}
.header-container div.selected { background-color: tomato;}
<script crossorigin src="https://unpkg.com/react@18/umd/react.production.min.js"></script>
<script crossorigin src="https://unpkg.com/react-dom@18/umd/react-dom.production.min.js"></script>
<div id='root'></div>
Upvotes: 1