Setting the state on one item only - reactjs

How can I reverse or toggle the state only the item clicked. So far all of the tab classes are toggling to active. But I need only the one clicked. I'm teaching myself hooks. so no class component solutions please
const App = () => {
const tabItems = [
{
id:"1",
tabname: "Unsplash",
datatab: "unsplash",
template: <Unsplash/>
},
{
id:"2",
tabname: "YouTube",
datatab: "youtube",
template: <Youtube/>
},
{
id:"3",
tabname: "Wikipedia",
datatab: "wiki",
template: <Wikipedia/>
},
{
id:"4",
tabname: "DropdownApp",
datatab: "dropdownapp",
template: <DropdownApp/>
},
]
const [activeTab, setActiveTab] = useState(false)
const tabs = tabItems.map((tab, i) => {
return (
<span
className={`item ${activeTab ? 'active' : ''}`}
key={tab.id}
data-tab={tab.datatab}
onClick={() => setActiveTab(!activeTab)}>
{tab.tabname}
</span>
)
})
const tabPanels = tabItems.map((tabPanel) => {
return (
<div
key={tabPanel.id}
className={`ui bottom attached tab segment ${activeTab ? 'active' : ''}`}
data-tab={tabPanel.datatab}>
{tabPanel.template}
</div>
)
})
return (
<div className="App">
<div className="ui main text" style={{padding: '20px'}}>
<div className="ui top attached tabular menu">
{tabs}
</div>
{tabPanels}
</div>
</div>
);
}

You are only tracking if the tab are selected or not, not which one, since you have one state for all tabs. You need to track, which tab is selected:
const App = () => {
const tabItems = [
{
id:"1",
tabname: "Unsplash",
datatab: "unsplash",
template: <Unsplash/>
},
{
id:"2",
tabname: "YouTube",
datatab: "youtube",
template: <Youtube/>
},
{
id:"3",
tabname: "Wikipedia",
datatab: "wiki",
template: <Wikipedia/>
},
{
id:"4",
tabname: "DropdownApp",
datatab: "dropdownapp",
template: <DropdownApp/>
},
]
const [activeTab, setActiveTab] = useState("") // Track the id
const tabs = tabItems.map((tab, i) => {
return (
<span
className={`item ${activeTab === tab.id ? 'active' : ''}`} // Check if the tab ids are the same
key={tab.id}
data-tab={tab.datatab}
onClick={() => setActiveTab(tab.id)}> // Save the id instead of a boolean
{tab.tabname}
</span>
)
})
const tabPanels = tabItems.map((tabPanel) => {
return (
<div
key={tabPanel.id}
className={`ui bottom attached tab segment ${activeTab === tab.id ? 'active' : ''}`}
data-tab={tabPanel.datatab}>
{tabPanel.template}
</div>
)
})
return (
<div className="App">
<div className="ui main text" style={{padding: '20px'}}>
<div className="ui top attached tabular menu">
{tabs}
</div>
{tabPanels}
</div>
</div>
);
}
If you want to unselect a tab, you would need to modify the onClick:
setTab = (id) => {
setActiveTab(tab => tab === id ? "" : id);
}
and invoke it with:
<span
onClick={() => setTab(tab.id)}>

Related

React vertical Nested List open and close sub List

i have a react.js component in which i am displaying states and under i am displaying districts. To display this list it's working fine. The problem i want when i press any sates only that particular state sublist should display not all states sublist.
import React,{useState} from "react"
Const [open,,setOpen]=useState(false);
//Wrapper component
</div>
{states.map((city, index) => {
return <StateList state={state} key={index} />;
})}
</div>
//state component
<div onClick={()=>setOpen(!open)}>
<span >{state.name}</span>
<svg
viewBox="0 0 24 24"
className={`
${open ? "rotate-180" : ""}
`}
>
</svg>
</h2>
{open && <AreaList area={city} />}
</div>
//district component
const AreaList = ({ state }) => {
return state.districts.map((district) => (
<li>
<span className="ml-2 text-outer-space">
{district.name}
</span>
</li>
));
};
Here is working solution (without styles):
Codesandbox
import { useState } from "react";
const data = [
{
name: "Fujairah",
districts: [
{ name: "Al Buthna" },
{ name: "Al Bedia" },
{ name: "Town Center" },
{ name: "Wadi Al Sedr" }
]
},
{
name: "Abu Dhabi",
districts: [{ name: "Al Aman" }, { name: "Al Bahya" }]
}
];
const App = () => {
return (
<div>
{data.map((city) => {
return <City key={city.name} city={city} />;
})}
</div>
);
};
const City = ({ city }) => {
const [open, setOpen] = useState(false);
return (
<div onClick={() => setOpen(!open)}>
<h2>
<span>{city.name}</span>
</h2>
{open && <DistrictList city={city} />}
</div>
);
};
const DistrictList = ({ city }) => {
return city.districts.map((district) => (
<li key={district.name}>
<span>{district.name}</span>
</li>
));
};
export default App;

repeater field repeating incorrectly

I want to click a button to add a tab and then have content inside the tab that can also be repeated. Currently the part where I click on the button to add a new tab is working however entering content in tab one duplicates it in tab 2 once I create another tab ie: the inside content of tab 1 and 2 seems linked when it shouldn't be. I should be able to create multiple tabs and then enter unique data inside each tab.
If I have explained this poorly please let me know and I will elaborate further. I think it perhaps needs to be an array of objects within an array of objects.
registerBlockType("blocks/tabs", {
title: __("Tabbed Blocks", "custom-blocks"),
description: __("Tabbed content blocks", "custom-blocks"),
icon: "smiley",
category: "custom-category",
keywords: [__("tabs", "custom-blocks"), __("repeat", "custom-blocks")],
attributes: {
tabs: {
type: "array",
default: [""],
},
tiles: {
type: "array",
default: [""],
},
},
edit: (props) => {
const {
attributes: { tabs, tiles },
setAttributes,
} = props;
const [showTab, setShowTab] = useState("");
const handleTabClick = (index) => {
console.log(index);
setShowTab(index);
};
return (
<>
<div className="tabs">
<ul id="tabs-nav">
{tabs.map((t, index) => (
<li>
<span onClick={() => handleTabClick(index)} id={`#tab${index}`}>
<RichText
placeholder="Tab title"
onChange={(tabTitle) => {
const newObject = Object.assign({}, t, {
tabTitle: tabTitle,
});
setAttributes({
tabs: [
...tabs.filter((item) => item.index != t.index),
newObject,
],
});
}}
value={t.tabTitle}
/>
</span>
</li>
))}
</ul>
{tabs.map((t, index) => (
<div
id={`tab${index}`}
className={
showTab == index ? `tab-content show` : `tab-content hide`
}
>
<div className="home-tabs">
{tiles.map((tile, index) => (
<div
className="tab-block"
>
<div className="copy">
<RichText
tagName="h3"
placeholder="Tab Content Title"
onChange={(tabTileTitle) => {
const newObject = Object.assign({}, tile, {
tabTileTitle: tabTileTitle,
});
setAttributes({
tiles: [
...tiles.filter(
(item) => item.index != tile.index
),
newObject,
],
});
}}
value={tile.tabTileTitle}
/>
<p>
Some content...
</p>
</div>
</div>
))}
</div>
</div>
))}
<Button
onClick={() => {
setAttributes({
tiles: [...tiles, { index: tiles.length }],
});
}}
>
Add Tile
</Button>
</div>
<Button
onClick={() => {
setAttributes({
tabs: [...tabs, { index: tabs.length }],
});
console.log(tabs);
}}
>
Add Tab
</Button>
</>
);
},
save: (props) => {
return null;
},
});

Function invoking only for first item React

i wrote code to expand "more info" block after clicking button, but function invoking only for first item.
Is it happening beacuse i use let more = document.getElementById("more"); ?
How can i change code for expanding only specifed item?
const Currency = ({ filteredItems, isLoading }) => {
const addListeners = () => {
let more = document.querySelectorAll(".more-info");
more.forEach(item => {
item.addEventListener("click", toggle)
})
console.log(more)
}
const toggle = () => {
let more = document.getElementById("more");
if (more.className === "more-info") {
more.className = "more-info-active";
} else {
more.className = "more-info";
}
}
return isLoading ? (<div className="loader">Loading...</div>) : (
<div items={filteredItems}>
{filteredItems.map((item) => (
<div key={item.id} className="item-wrapper">
<div className="item">
<h2>{item.name}</h2>
<img src={item.image} alt="crypto symbol"></img>
<h3>{item.symbol}</h3>
<p>{item.current_price} pln</p>
<button onLoad={addListeners} onClick={toggle} className="info-btn" id="item-btn" >➜</button>
</div>
<div id="more" className="more-info">
<div className="more-data">
<div className="info-text">
<p>high_24: {item.high_24h}</p>
<p>low_24: {item.low_24h}</p>
</div>
<div>
<p>price_change_24h: {item.price_change_24h}</p>
<p>price_change_percentage_24h: {item.price_change_percentage_24h}</p>
</div>
<div>
<Sparklines className="sparkline" height={60} margin={10} data={item.sparkline_in_7d.price}>
<SparklinesLine style={{fill:"none"}} color="#b777ff" />
</Sparklines>
</div>
</div>
</div>
</div>
))}
</div>
);
}
Dont use document.getElement... , this is a Real DOM but React uses Virtual DOM.
Instead create a state with an array and on onClick event pass item as an argument and store in state , you can store only id e.g.
Last step, check in JSX if state includes item.id , if true then expand
this is an example , keep in mind this is not the only solution. Just simple example.
import React, { useState } from "react";
const fakeData = [
{
id: "123123-dfsdfsd",
name: 'Title One',
description: "Description bla bla bla One"
},
{
id: "343434-dfsdfsd",
name: 'Title Two',
description: "Description bla bla bla Two"
},
{
id: "6767676-dfsdfsd",
name: 'Title Three',
description: "Description bla bla bla Three"
}
]
function App() {
const [tabs, setTabs] = useState([]);
function _onToggle(item) {
const isExist = tabs.includes(item.id)
if (isExist) {
setTabs(prevData => prevData.filter(pd => pd !== item.id))
} else {
setTabs(prevData => [item.id, ...prevData])
}
}
return (
<div className="app">
<div>
{
fakeData.map((item, i) => (
<div key={i}>
<h3 onClick={() => _onToggle(item)}>{item.name}</h3>
<p style={{ display: tabs.includes(item.id) ? 'block' : 'none' }}>
{ item.description }
</p>
</div>
))
}
</div>
</div>
);
}
export default App;

How to toggle to show and hide in elements in Reactjs?

how to expand the elements present inside li . I used toggle on selected
I tried to add a default propertied selected false then onClick of the li I ensures if the values matches then selected should be true and displays that particular section ,and onClick of same it should collapse
Is it possible to open always the first dropdown remaining should be closed and when clicked other it open another li dropdown.
Currently onClick is not working
Here is my code
Demo
export default function App() {
const filterAddition = X.map((item) => ({
...item,
menus: item.menus.map((items) => ({
...items,
selected: false
}))
}));
const handleOnClick = (event) => {
filterAddition.map((item) => {
item.menus.map((items) => {
if (items.El=== event.target.value) {
return {
...items,
selected: !item.selected
};
}
});
});
};
return (
<div className="App">
<h1>Hello CodeSandbox</h1>
<h2>Start editing to see some magic happen!</h2>
<nav className="animated bounceInDown">
{filterAddition.map(({ menus }, idx) => {
return menus.map(({ El, subSection, selected }, idx) => (
<ul key={idx}>
<li className="sub-menu" value={El} onClick={handleOnClick}>
<a href="#settings">
{El}
<div class="fa fa-caret-down right"></div>
</a>
{selected && (
<ul>
{subSection.map(({ E }, i) => (
<li key={E}>
{E}
</li>
))}
</ul>
)}
</li>
</ul>
));
})}
</nav>
</div>
);
}
So much to change.
First, you have to use useState to make component reactive when data is change.
Second, onClick have to change.
Third, try to read documentation especially about react hooks, state and lifecycle.
see this https://codesandbox.io/s/pensive-cloud-9mco1?file=/src/App.js
import React, { useState, useEffect } from "react";
import "./styles.css";
const X = [
{
detail1: "FirstJob",
menus: [
{
Order: 1,
El: " Record Management",
subSection: [
{
E: "Check Notification",
Order: "CheckNotification"
},
{
E: "Check Record",
Order: "CheckRecord"
}
]
},
{
Order: 2,
El: "Management",
subSection: [
{
E: "Notification",
Order: "Notification"
},
{
E: "Check",
Order: "Check"
}
]
}
]
}
];
export default function App() {
const [state, setState] = useState(X);
useEffect(() => {
const filterAddition = X.map((item) => ({
...item,
menus: item.menus.map((items) => ({
...items,
selected: false
}))
}));
setState(filterAddition);
}, []);
const handleOnClick = (label) => {
const temp = JSON.parse(JSON.stringify(state));
for(let i in temp) {
const menus = temp[i].menus;
for(let j in menus) {
const item = temp[i].menus[j];
if(item.El === label) {
item.selected = !item.selected;
}
}
}
setState(temp);
};
return (
<div className="App">
<h1>Hello CodeSandbox</h1>
<h2>Start editing to see some magic happen!</h2>
<nav className="animated bounceInDown">
{state.map(({ menus }, idx) => {
return menus.map(({ El, subSection, selected }, idx) => (
<ul key={idx}>
<li className="sub-menu" value={El} onClick={() => handleOnClick(El)}>
<a href="#settings">
{El}
<div className="fa fa-caret-down right"></div>
</a>
{selected && (
<ul>
{subSection.map(({ E }, i) => (
<li key={E}>
{E}
</li>
))}
</ul>
)}
</li>
</ul>
));
})}
</nav>
</div>
);
}

Creating a custom Modal for Material-Table

I am trying to create a custom button on my Material-Table that will pop up a model that will allow users to make changes. The issue I am running into is the button is not toggling to true when clicked upon. I am using a custom Modal hook. The modal itself work, because when it is explicitly set to true it pops up on the screen
here is the modal hook
const useModal = () => {
const [isShowing, setIsShowing] = useState(false);
function toggle() {
setIsShowing(!isShowing);
}
return {
isShowing,
toggle,
}
};
the modal component
const Modal = ({ isShowing, hide }) => isShowing ? ReactDOM.createPortal(
<React.Fragment>
<div className="modal-overlay"/>
<div className="modal-wrapper" aria-modal aria-hidden tabIndex={-1} role="dialog">
<div className="modal">
<div className="modal-header">
<button type="button" className="modal-close-button" data-dismiss="modal" aria-label="Close" onClick={hide}>
<span aria-hidden="true">×</span>
</button>
</div>
<p>
Hello, I'm a modal.
</p>
</div>
</div>
</React.Fragment>, document.body
) : null;
and finally the actual table component
columns: [
{ title: "Department", field: "department" },
{ title: "Security", field: "security", type: "numeric" },
{ title: "Status", field: "status" },
{
title: "Actions",
field: "actions",
render: rowData => (
<div className="edit-button">
<span className={" fas fa-pencil-alt"} onClick={toggle} />
{console.log(isShowing)}
<Modal isShowing={isShowing} hide={toggle} />
</div>
)
}
],
data: [{ department: "Admin", security: 10, status: "Active" }]
});
<MaterialTable
title="Departments"
columns={state.columns}
data={state.data}
editable={{
onRowAdd: newData =>
new Promise(resolve => {
setTimeout(() => {
resolve();
setState(prevState => {
const data = [...prevState.data];
data.push(newData);
return { ...prevState, data };
});
}, 600);
})
}}
/>
I need the isShowing to toggle between true and false

Resources