Is it possible to pass multiple functions to navigation items? - javascript

I'm quite new to React.js and I need to add a logout link to a hamburger menu. In the parent component there is already defined an onClick -function for all the NavLinks, but I have the new logout button there which needs to be passed the logout-function instead. I'm not quite sure how to achieve that. I've tried several approaches that came to my mind, but they either were fired immediately when the nav was loaded or crashed the component.
The Nav is a an array of objects. A couple of NavLink items are visible only on mobile sizes and that part I got working already, but how to make the log out link work only when clicking that specific item?
Any help is appreciated.
return (
<li className={liClassnames}>
<OTNavLink
activeClassName="is-active"
disabled={disabled}
link={link}
isExternal={isExternal}
to={link}
className={linkClassNames}
/*onClick={(e) => { // This code was here originally
toggleNavigation(e);
}}*/
onClick={(e) => { // And this is how I last tested whether altering this
SessionUtils.logout(e); // changes the links behaviour
}} // it naturally did for all the links
{...linkAttributes}
>
Here is the OTNavLink -components code you asked :)
const OTNavLink = (props) => {
let {link, isExternal = false, disabled, children, ...rest} = props;
let restForAnchor = omit(rest,["activeClassName", "disabled", "to"]);
if (disabled) {
return (
<a {...restForAnchor}>
{children}
</a>
)
}
// unwrap link (this should be unnecessary but applies to some cases)
let _link = isEmpty(link) ? "javascript:void();" : isFunction(link) ? link() : link;
if (isExternal) {
return (
<a
href={_link}
{...restForAnchor}
>
{children}
</a>
)
} else {
return (
<NavLink
to={_link}
{...rest}
>
{children}
</NavLink>
)
}
};
And here's an example of the NavigationItems array:
It consists of multiple similar objects. some are rendered on mobile and hidden in desktop.
let navigationItems = [
{
message: "navigation.timeline",
disabled: false,
icon: {
type:"custom",
name:"my-name"
},
link: "/timeline",
key: "timeline"
},
How can I pass a different function call to that one last logout link?
If you need any more details let me know :)

I think I managed a solution myself in the end. It was so simple all along. Although someone can tell me if there is a better way. :)
I just made a ternary check in the custom component, so it will apply to only the links that have that message property.
onClick={this.props.message === 'logout' ? (e) => SessionUtils.logout(e) : (e) => {
toggleNavigation(e);
}}

Related

How to render a new popup every time I clicked Grid?

The problem is...
The first popup renders fine.
But when I try to render the second popup, it's not working.
A new popup is not invoked, the previous popup is refreshed.
I want to call a new popup when I clicked a cell in the grid.
my code is like this
const Main = () => {
const [isPopupOpen, setIsPopupOpen] = useState(false);
return (
<>
... other components (including grid)
{ isPopupOpen && <Popup />}
</>
)
};
when Grid is Clicked, 'isPopupOpen' is updated to true.
I use 'react-new-window' library, and this library use 'window.open()' ((https://github.com/rmariuzzo/react-new-window)
so I set different window names to call several popups.
but I can't solve the problem.
I try to set a state object that has a boolean value.
const [popupObj, setPopupObj] = useState({});
when the grid is clicked, popupObj updates like
{'cellA': true, 'cellD': true}
and a return statement is like
{popupObj[cellName] && <Popup /> }
but the result was the same.
what should I do to solve this problem?
I wrote an example for you. Hope it helps.
use popupIds state to store the popups that you want to open
use Set to toggle the popupIds in the addPopup click handler
import * as React from "react";
export default function App() {
const [popupIds, setPopupIds] = React.useState([]);
const addPopup = (popupId) => {
const set = new Set(popupIds);
if (set.has(popupId)) {
set.delete(popupId);
} else {
set.add(popupId);
}
setPopupIds(Array.from(set));
};
return (
<div className="App">
{["hello", "react"].map((popupId) => (
<div onClick={() => addPopup(popupId)}>{popupId}</div>
))}
{popupIds.map((popupId) => (
<Popup title={getPopupTitle(popupId)} />
))}
</div>
);
}
const getPopupTitle = (popupId) => `title for ${popupId}`;
const Popup = ({ title }) => <div>{title}</div>;
Here is a codesandbox that you can play with directly.
You need to add your popup in an array, so you can render many popup as you want, then you need to define in How much time you will remove a added popup from array or add a close button
Extra: you can configure in global state to access in all your application to your popups and you will have a component like this: https://www.npmjs.com/package/notistack

React Link to open new tab

I Have a React link in my material table.
actions={[
rowData => ({
icon: () => <Link style={{ fontSize:"15px" ,fontSize:'15px'}} to={{pathname:'/VV',state: rowData }} >View</Link> ,
onClick: (rowData)
})
]}
I want to be able to open a new tab on the click.
But my child objects keep getting Cannot read properties of undefined when i decide to open it in a new tab
Please can i have some assistance i've been stuck on this problem.
There is a simple way you can do that without React-Router library,
Make a Link component like blow and use it.
const Link = () => {
const linkRef = useRef(null)
const handleClick = () => {
linkRef.current.link.click()
}
return (
<div ref={linkRef} onClick={handleClick}>
Go to google!
</div>
)
}
You can put the link you want in href and render the Link component where you want! also you can give it a style you want cuz it's a component! :D
I hope you find an answer!

React: useState not preserving updated state upon click

Update: I was truly trying to reinvent the wheel here, the code below works if you use it in conjunction with React's Link or NavLink instead of anchor tags, it has built-in listening functionality that will keep track of the page you are currently on and pass along the updated state accordingly as your route changes to a different page.Thank you to everyone that chimed in and pointed me in the right direction!
I'm still fresh off the block with React, especially with hooks, but what I'm trying to accomplish is to trigger the 'active' tab class of my navbar elements through conditional rendering and managing state with useState.
However, when I call 'setActiveTabIdx' upon click, I can't tell if it's not updating state at all, or if it is and is resetting to the default value upon re-render. I was trying to use my dev tools to monitor upon click but it's happening too fast for me to say one way or the other. I've messed around a fair bit at this point trying a number of different things if anyone would be willing to take a look, thanks!
const NavBar = () => {
const [activeTabIdx, setActiveTabIdx] = useState(0)
const navItems = ['About', 'Services', 'Oils', 'New Patients', 'Courses', 'Recommendations', 'Contact' ]
const renderedItems = navItems.map((nav, idx) => {
const active = idx === activeTabIdx ? 'active' : ''
return (
<a
onClick={() => setActiveTabIdx(idx)}
href={`${nav.split(' ').join('').toLowerCase()}`}
className={`${active} item`}
key={idx}
>{nav}</a>
)
})
return (
<div className="ui tabular menu">
{renderedItems}
</div>
);
};
export default NavBar;
You are trying to change state (which is done) and then redirect user to other page (which is also done). The state resets after redirection.
It seems it is resetting, I added this block to check:
const NavBar = () => {
const [activeTabIdx, setActiveTabIdx] = useState(0)
const navItems = ['About', 'Services', 'Oils', 'New Patients', 'Courses', 'Recommendations', 'Contact' ]
// --------- Start block ---------
useEffect(() => {
console.log(`current state: ${activeTabIdx}`);
}, [activeTabIdx]);
// --------- End block ---------
const renderedItems = navItems.map((nav, idx) => {
const active = idx === activeTabIdx ? 'active' : ''
return (
<a
onClick={() => setActiveTabIdx(idx)}
href={`${nav.split(' ').join('').toLowerCase()}`}
className={`${active} item`}
key={idx}
>{nav}</a>
)
})
return (
<div className="ui tabular menu">
{renderedItems}
</div>
);
};
export default NavBar;
And to check codesandbox

How to switch the state of buttons in react hooks after updating it through rest api?

Hey guys I am very new to react and trying to make the frontend of a blog web application. I am able to show the posts on the homepage and I am able to make the like button work without API calls, just with managing states.
Now with API call, the like button shows red(button fills with red) if the post is liked by the user and I am able to unlike it by clicking it, it changes the count and it unlike the post in the backend, but it doesn't change the button state to unlike button and it keeps on unliking it rather than switching to like button state.
If the post is not liked by the user, then the button completely disappears and doesn't show on the screen, so I am not able to like the post.
This is the code I have written, It is not a good way to write react code I think, If anyone can help resolve this issue, it would be highly enlightening as I am still learning. Please do ask for more information if needed.
This is the code.
const [liked, setLiked] = useState(null)
function setlikeCount(post){
return(
post.like_count = post.like_count + 1
)
}
function setunlikeCount(post){
return(
post.like_count = post.like_count - 1
)
}
function likePosts(post) {
console.log('liked the post')
return(
axiosInstance.post('api/posts/' + post.slug + '/like/')
)
}
function unlikePosts(post) {
console.log('unliked the post')
return(
axiosInstance.delete('api/posts/' + post.slug + '/like/')
)
}
{myposts.posts && myposts.posts.results.map((post) => {
return (
<h4>{post.title}</h4>
)
}
{post.likes && post.likes.map((lik, index) => {
console.log(user, lik.id)
return (
user === lik.id ? (<FavoriteRoundedIcon style={{ color: "red" }}
key={index}
onClick={ () =>{
unlikePosts(post)
setunlikeCount(post)
setLiked((liked) => liked===false)
}}
/>)
: (<FavoriteBorderRoundedIcon key={index}
onClick={ () =>{
likePosts(post)
setlikeCount(post)
setLiked((liked)=> liked===true)
}}
/>)
)
})
}
const [myposts, setPosts] = useState({
posts: null,
})
fetching posts
useEffect(() => {
axiosInstance.get('api/posts/myhome').then((res) => {
const allPosts = res.data;
setLoading(false)
setError("")
setPosts({ posts: allPosts })
// console.log(allPosts.results['0'].likes['0']);
})
.catch(() => {
setLoading(false)
setPosts({})
setError('Something went wrong!')
})
}, [setPosts])
In the code, the user has the user's id.
Is it possible to check the condition like user in lik.id than user === lik.id, like how we check conditions in python?
lik looks like this [{id: 1, username: "testuser12"}]
Thanks
You need to show the button based on the content of the array like below
{post.likes && post.likes.find(x=>x.id===user) ?
(<FavoriteRoundedIcon style={{ color: "red" }}
key={index}
onClick={ () =>{
unlikePosts(post)
setunlikeCount(post)
setLiked((liked) => liked===false)
}}
/>)
: (<FavoriteBorderRoundedIcon key={index}
onClick={ () =>{
likePosts(post)
setlikeCount(post)
setLiked((liked)=> liked===true)
}}
/>)
}
If the array has values and the user is part of the array you show red button and if the array is not defined or user is not in the array you show the other button.
Firstly, your setLiked method isn't right. if you want to set it to true/false just call:
setLiked(true)
Secondary, you should init your liked state. Meaning you need to useEffect (when the component loads) and read from your API if post liked or not. But the initial value better to be false and not null.

React: override internal components with custom component

I have a modal that is completely self contained. The modal is opened via going to the modal route and all the functionality to close the modal from button or outside clicks is within the modal component. Basically the modal is not controlled by any parent passing state. I was given a task of making the modals button customizable, meaning passing in a new button component, so we can add the modal to our lib instead of copy pasting the code in projects. Lol this seemed simple enough, and maybe it is and I am just overthinking this.
I cant paste the actual code but I can use a contrived example. This is a very simplified version of the modal, keeping in mind it opens via route so there's really no state and setState in the actual code. Also here is a fiddle
const ModalHeader = ({ onClose }) => {
return (
<div className="modal__header">
<button
className="modal__close-btn"
data-testid="modal-close-button"
onClick={onClose}
/>
</div>
);
};
const Modal = ({ children }) => {
const [state, setState] = React.useState(true);
const handleCloseOutsideClick = () => {
setState(false);
};
const handleCloseButtonClick = () => {
setState(false);
};
const renderModal = () => {
return (
<div className="modal-overlay" onClick={handleCloseOutsideClick}>
<div className="modal">
<ModalHeader onClose={handleCloseButtonClick} />
{children}
</div>
</div>
);
};
return state ? renderModal() : null;
};
const App = () => {
return (
<Modal>
<div>Modal Children</div>
</Modal>
);
};
ReactDOM.render(<App />, document.querySelector('#app'));
I tried a few things, initially I attempted to find a way to pass in a new header component containing a button. Then as I got into the code I realized what I was doing would lose the self contained functionality of the modal. My approach was along the lines of below but obviously the onClick would be an issue since invoking the close functionality is internal.
So I tried using cloneElement to add props within the component if the custom header was detected:
// inside modal component
React.useEffect(() => {
React.Children.map(children, (child: React.ReactElement) => {
if (child && child.type === ModalHeader) {
setHederFound(true);
}
});
}, []);
// inside modal render:
<div className={modalClasses} onClick={stopPropagation}>
{!headerFound ? (
<ModalDefaultHeader onClose={handleCloseButtonClick} />
) : (
React.Children.map(children, (child: React.ReactElement) => {
if (child && child.type === ModalHeader) {
return React.cloneElement(child, {
onClose: handleCloseButtonClick,
});
}
})
)}
{children}
</div>;
Obviously that did not work because there's no onClick in the custom button. Anyways I am thinking that I am over complicating this. I just need a way to pass in a custom button while leaving the functionality internal to the modal. Any assistance would be appreciated.
Thanks in advance.

Categories

Resources