How to show a Modal once onShowMoreClick is clicked? - javascript

<OneProfileKeyCard
title="Qualification"
showMoreText="See all qualifications"
onShowMoreClick={() => console.log('show more')}
>
Creating, communicating, and implementing the organization&apos;s vision, mission, and overall direction Leading the development and implementation of the overall organization&apos;s strategy.
</OneProfileKeyCard>
import React from 'react'
import './OneProfileKeyCard.scss'
type Props = {
title: string
showMoreText: string
onShowMoreClick: () => void
}
export const OneProfileKeyCard: React.FC<Props> = ({
title,
showMoreText,
onShowMoreClick,
children
}) => (
<div className="one-profile-key-card">
<h3>{ title }</h3>
<div>
{ children }
</div>
<button type="button" onClick={onShowMoreClick}>
{ showMoreText }
</button>
</div>
)
could anyone help me to set up a modal? Im trying to set up a modal once onShowMoreClick is clicked that would turn the children(creating, communicating, and implementing the organization...) into a modal. So far it looks like this:

You will need to have a state-managed in the parent component of where the OneProfileKeyCard child component is called from.
Something like this
const Parent = () => {
const [modalOpen, setModalOpen] = React.useState(false)
return (
<div>
<h1>Demo</h1>
<OneProfileKeyCard
title="Qualification"
showMoreText="See all qualifications"
onShowMoreClick={() => setModalOpen(!modalOpen)}>
text ... text
</OneProfileKeyCard>
</div>
)
}
I'm not sure what else is within your components, but you'll then need a way to close the model, right now I have set the showMoreClick prop to open/close, but if that should open then set it to true and do a similar pass-through for a closing false function.

Related

how to keep the last component styles?

i'm building a to-do app using React Js . inside the task component i used a state to apply a certain styles for the completed task and it works fine . but , after i cliked any delete button the style of the completed task deleted . how can i prevent that ?
import Task from "../Task/Task";
import style from "./TasksList.module.css";
const TasksList = ({ tasks, deleteTaskHandler }) => {
return (
<div className={style.tasks}>
<div className="container">
{tasks.map((task, idx) => {
return (
<Task
task={task}
id={idx}
key={Math.random()}
deleteTaskHandler={deleteTaskHandler}
/>
);
})}
</div>
</div>
);
};
export default TasksList;
import { useState } from "react";
import style from "./Task.module.css";
const Task = ({ task, id, deleteTaskHandler }) => {
const [isComplete, setIsComplete] = useState(false);
const markComplete = () => {
setIsComplete(!isComplete);
};
return (
<div
className={
isComplete ? `${style.task} ${style.completed}` : `${style.task}`
}
onClick={markComplete}
>
<label>{task.desc}</label>
<button onClick={() => deleteTaskHandler(id)}> Delete </button>
</div>
);
};
export default Task;
How are you maintaining the complete status of task in higher components?
Currently you are not initializing the complete state of Task.
If the task object contains the isComplete property, then you can use as shown below
const [isComplete, setIsComplete] = useState(task.isComplete);
however, you also need to update value of completed in task. So, I would suggest to have all lower components as stateless. and maintain the state at Higher component i.e. TaskList
import style from "./Task.module.css";
const Task = ({ task, id, deleteTaskHandler, setTaskCompleteHandler }) => {
const markComplete = () => {
setTaskCompleteHandler(!task.isComplete);
};
return (
<div
className={
task.isComplete ? `${style.task} ${style.completed}` : `${style.task}`
}
onClick={markComplete}
>
<label>{task.desc}</label>
<button onClick={() => deleteTaskHandler(id)}> Delete </button>
</div>
);
};
export default Task;
Implement setTaskCompleteHandler in TaskList and pass is as prop as part of Task component reandering.
Your problem is in position of your delete button, that wrapped by div with makrComplete handler, so then you click on your delete button, markComplete fired too, so your isComplete changed and styles deleted.
To prevent this behavor, you can do a little trick with prevent default. So, try to wrap your deleteTaskHandler in another function like that:
const deleteButtonClickHandler = (e) => {
e.preventDefault();
e.stopPropagation();
deleteTaskHandler(id)
}
and your delete button makrdown should be look like this:
<button onClick={deleteButtonClickHandler}> Delete </button>

onClick function won't fire for mapped component

Here is the relevant code:
const Members = () => {
// array of each video in selected grade
const videosMap = (videos) => {
return videos.map((video) => (
<VideoCard
key={video.id}
thumbnail={video.thumbnail}
title={video.title}
description={video.description}
onClick={() => {
handleVideoClick();
}}
/>
));
};
// updates state of shown videos & page heading
const handleGradeButtonClick = (videos, heading) => {
setShowVideos(videosMap(videos));
setVideosHeading(heading);
};
const handleVideoClick = () => {
console.log("test");
};
// controls state of which grade's videos to show
const [showVideos, setShowVideos] = useState(videosMap(kinder_videos));
// controls states heading to display depending on selected grade
const [videosHeading, setVideosHeading] = useState("Kindergarten");
const [showVideoDetails, setShowVideoDetails] = useState(null);
The handleVideoClick is the function that is not working when I click on one of the mapped VideoCard components.
Here is the full code if you want to see that:
https://github.com/dblinkhorn/steam-lab/blob/main/src/components/pages/Members.js
When I look in React DevTools at one of the VideoCard components, it shows the following:
onClick: *f* onClick() {}
If I don't wrap it in an arrow function it does execute, but on component load instead of on click. I have a feeling it has something to do with my use of .map to render this component, but haven't been able to figure it out.
Thanks for any help!
There's no problem with your mapping method, you just need to pass the onClick method as a prop to your VideoCard component :
On your VideoCard component do this :
const VideoCard = (props) => {
const { thumbnail, description, title, onClick } = props;
return (
<div className="video-card__container" onClick={onClick}>
<div className="video-card__thumbnail">
<img src={thumbnail} />
</div>
<div className="video-card__description">
<div className="video-card__title">
<h3>{title}</h3>
</div>
<div className="video-card__text">{description}</div>
</div>
</div>
);
};
export default VideoCard;

What is the React way of inserting an icon into another component?

I'm trying to create an WithIcon wrapper component which would insert a child (icon) into a wrapped component.
Let's say I have a button:
<Button>Add item</Button>
I want to create a component WithIcon which will be used like this:
<WithIcon i="plus"><Button>Add item</Button></WithIcon>
Ultimately what I want to achieve is this:
<Button className="with-icon"><i className="me-2 bi bi-{icon}"></i>Add item</Button>
Notice the added className and the tag within the Button's body.
I'm trying to figure out how the WithIcon component's code should look like. What is the React way of achieving this result?
The hardest part was the rules of using the WithIcon Will we only have one ?
Will we have only it at the leftmost ? Something like that.
But if we follow your example. We can relatively write something like this for the WithIcon
const WithIcon = ({ i, children }) => {
return React.Children.map(children, (child) => {
return (
<>
<i className={`me-2 bi bi-${i}`}></i>
{React.cloneElement(child, { className: "with-icon" })}
</>
);
});
};
Then we can just use it the way you want it
<WithIcon i="plus"><Button>Add item</Button></WithIcon>
What we do is just looping through the children which in react is any nested jsx you throw in it (Button in our case)
You can find my fiddle here : https://codesandbox.io/s/react-font-awesome-forked-321tz?file=/src/index.js
UPDATE
So my previous answer does not fully meet the end result we want. The will need to be the main parent
The idea is still quite the same as before but here we are infering the type of the component we passed inside the WithIcon This also adds a safeguard when we passed a nested component inside the WithIcon
const WithIcon = ({ i, children }) => {
return React.Children.map(children, (child) => {
const MyType = child.type; // So we can get the Button
return (
<MyType className="with-icon">
<i className={`me-2 bi bi-${i}`}></i>
{(React.cloneElement(child, {}), [child.props.children])}
</MyType>
);
});
};
I think I'll go to sleep I'll update the rest of the explanation at later date.
See the fiddle here :
https://codesandbox.io/s/react-font-awesome-forked-y43fx?file=/src/components/WithIcon.js
Note that this code does not preserved the other props of the passed component, but you can relatively add that by adding {...child.props} at the MyComponent which is just (reflection like?) of infering the component.
Of course also have another option like HOC Enhancers to do this but that adds a bit of complexity to your how to declare your component api. So Pick whats best for ya buddy
Maybe try using a higher order component?
const withIcon = (icon, Component) => ({children, ...props}) => {
return (
<Component className="with-icon" {...props}>
<i className=`me-2 bi bi-${icon}` />
{children}
</Component>
);
}
Then the usage is
const ButtonWithIcon = withIcon("your-icon", Button);
<ButtonWithIcon>Add Item</ButtonWithIcon>
From my experience with react it usually comes down to either using a property inside the component like here (https://material-ui.com/api/button/) or higher order component like what I described.
There are two common patterns used in React for achieving this kind of composition:
Higher-Order Components
Start by defining a component for your button:
const Button = ({ className, children }) => (
<button className={className}>{children}</button>
);
Then the higher-order component can be implemented like this:
const withIcon = (Component) => ({ i, className = '', children, ...props }) => (
<Component {...props} className={`${className} with-icon`}>
<i className={`me-2 bi bi-${i}`} />
{children}
</Component>
);
Usage:
const ButtonWithIcon = withIcon(Button);
<ButtonWithIcon i="plus">Add Item</ButtonWithIcon>
Context
Start by defining the context provider for the icon:
import { createContext } from 'react';
const Icon = createContext('');
const IconProvider = ({ i, children }) => (
<Icon.Provider value={i}>{children}</Icon.Provider>
);
and then your component:
import { useContext } from 'react';
const Button = ({ className = '', children }) => {
const i = useContext(Icon);
if (i) {
className += ' with-icon';
children = (
<>
<i className={`me-2 bi bi-${i}`} />
{children}
</>
);
}
return (
<button className={className}>{children}</button>
);
};
Usage:
<IconProvider i="plus"><Button>Add Item</Button></IconProvider>

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

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