Custom icon wrapper component - reactjs

I want to use react-feather icons but within a standard size button component I created.
To use a react-feather component I need to create it like so <Camera size={18} /> or <Icon.Camera size={18} /> however, I don't want to keep on repeating the size and color and any other prop that stays the same.
How can I make the icon type dynamic within my Button.
I know this form is invalid but I am looking for a way that is and that is the best I could write to present my thought.
import React, { FC } from "react";
import * as Icons from "react-feather";
interface IconProps {
icon?: Icons.Icon;
}
const IconComponent: FC<IconProps> = (props) => {
return (
<button>
{props.icon && <Icons[props.icon] size={18} />}
{props.children}
</button>
);
};
export default IconComponent;

The IconComponent component below should do. You can see it live in this codesandbox.
// Icon.tsx
import React, { ReactNode } from "react";
import { Icon } from "react-feather";
interface IconComponentProps {
icon?: Icon;
children: ReactNode;
}
const IconComponent = ({ icon: FeatherIcon, children }: IconComponentProps) => {
return (
<button>
{FeatherIcon && <FeatherIcon size={18} />}
{children}
</button>
);
};
export default IconComponent;
There you could use it anywhere:
// App.tsx
import IconComponent from "./Icon";
import { Camera, Airplay } from "react-feather";
import "./styles.css";
export default function App() {
return (
<div className="App">
<IconComponent icon={Camera}>
<h1>Camera</h1>
</IconComponent>
<IconComponent icon={Airplay}>
<h1>Airlpay</h1>
</IconComponent>
<IconComponent>
<h1>No Icon</h1>
</IconComponent>
</div>
);
}

Related

'X' is declared but its value is never read. Having trouble understanding where to assign it

Button.tsx
import React from "react";
interface ButtonProps {
name: string;
onButtonClick: () => void;
}
function Button(props: ButtonProps) {
const { name, onButtonClick } = props;
function buttonAlert() {
alert(`Testing`);
}
return (
<div>
<button className="buttonOne" onClick={buttonAlert}>{name}</button>
</div>
);
}
export default Button;
index.tsx
import React from 'react';
import ReactDOM from 'react-dom';
import './index.css';
import App from './App';
import reportWebVitals from './reportWebVitals';
import Button from './Button';
ReactDOM.render(
<React.StrictMode>
<App />
<Button name="Button One" onButtonClick={() => { alert(`You clicked on Button One! 👆🏻`); }} />
<Button name="Button Two" onButtonClick={() => { alert(`You clicked on Button Two! 👆🏻`); }} />
<Button name="Button Three" onButtonClick={() => { alert(`You clicked on Button Three! 👆🏻`); }} />
</React.StrictMode>,
document.getElementById('root')
);
Okay so I am trying 9 Simple (But Important) React JS Coding Exercises You Need To Do As A Beginner and am currently on exercise three. I was asked to use TypeScript so I did add it to my project. I just had a meeting where I was told to use onButtonClick as a type and pass it as a prop in my <Button /> component. Right now, what I do not understand and am having trouble figuring out is, where on earth do I pass the onButtonClick in my Button.tsx component. Which is why I am getting the message that I have declared it but I haven't used it. Could someone help me understand where exactly I am supposed to pass it?
Thank you!
The thing is you're not using the onButtonClick prop anywhere in your Button component. You have to use it instead of the hard-coded buttonAlert function that always shows the same message.
Button.tsx
import React from "react";
interface ButtonProps {
name: string;
onButtonClick: () => void;
}
function Button(props: ButtonProps) {
const { name, onButtonClick } = props;
/* Remove the hard-coded alert
function buttonAlert() {
alert(`Testing`);
}*/
/* Change buttonAlert on onClick to the onButtonClick prop */
return (
<div>
<button className="buttonOne" onClick={onButtonClick}>{name}</button>
</div>
);
}
export default Button;

Dynamic loading icon

Hi have a script that import location, which consists of multiple svg file and a index.ts file to import and export them.
import * as Icons from '../../icons'
I then have a functional component that returns the icon based on the icon name, which I am getting error.
interface props extends React.SVGProps<SVGSVGElement> {
icon: string
title?: string | undefined
}
function Icon({ icon, ...props }: props) {
const Icon = Icons[icon] <= error here
return <Icon {...props} />
}
function SidebarContent() {
return (
//some code here
<Icon className="w-5 h-5" aria-hidden="true" icon={route.icon} />
//some code here
)
}
The error I am getting is this:
Element implicitly has an 'any' type because expression of type 'string' can't be used to index type 'typeof import("d:/Code/JS/meal-order/frontend/src/icons/index")'.
No index signature with a parameter of type 'string' was found on type 'typeof import("d:/Code/JS/meal-order/frontend/src/icons/index")'.ts(7053)
I would like to know how in the Icon component. How can it accept icon name (Icon[icon]) and return the correct icon? or probably there is other way to do this ?
More info:
If I write the code like the following then it works without complaining. However that doesn't fit the purpose because my intention is to dynamically load the icon based on the icon name i've passed.
function Icon({ icon, ...props }: props) {
const Icon = Icons.ButtonsIcon
return <Icon {...props} />
}
Update:
This is how I fix the type error:
import react from "react";
import * as Icons from "../../icons";
type IIcon = React.FunctionComponent<React.SVGProps<SVGSVGElement> & {
title?: string | undefined;
}>
export default function Icon({ icon = 'MenuIcon', ...props }) {
const myObj: { [index: string]: IIcon } = Icons
const NewIcon = myObj[icon]
return <NewIcon {...props} />
}
Updated:
Here's a CodeSandbox of what I believe you're trying to do.
Icon.js
import react from "react";
import * as Icons from "./icons";
export default function Icon({ icon, ...props }) {
const NewIcon = Icons[icon];
return <NewIcon {...props} />;
}
Icons/index.js
import { ReactComponent as ButtonIcon } from "./button.svg";
import { ReactComponent as BellIcon } from "./bell.svg";
export { BellIcon, ButtonIcon };
App.js
import Icon from "./Icon";
export default function App() {
return (
<div className="App">
<Icon icon="BellIcon"></Icon>
<Icon icon="ButtonIcon"></Icon>
<h2>Start editing to see some magic happen!</h2>
</div>
);
}
https://codesandbox.io/embed/frosty-stitch-li3wiz?fontsize=14&hidenavigation=1&theme=dark
If you posted a codesandbox I could assist better that way, however, as long you Icons are components themselves, not just SVGs, it should work.

Cannot override styles with styled component in react-phone-input-2

Styles can be overridden with additional CSS file imported but not with the styled component. Why?
import React from "react";
import styled from "styled-components/macro";
import PhoneInput from "react-phone-input-2";
import "react-phone-input-2/lib/style.css";
const Phone = styled(PhoneInput)`
#phone-input {
background: red !important;
}
`;
function InputMobile({value}) {
function change(value) {
...
}
return (
<Phone
country={"us"}
inputProps={{
id: "phone-input"
}}
value={value}
onChange={change}
/>
);
}
export default InputMobile;
Such properties as dropdownStyle, inputStyle are not enough for me as I also need to style .arrow in .selected-flag
To use styled-component with custom component you need to wrap it. Example:
const Wrapper = ({ className, ...props }) => (
<div className={className}>
<PhoneInput {...props} />
</div>
);
You can test it live:

How to render children as string in React, in reusable component

I have created reusable component in React- reusableToolbar which I want to render some text inside, which is children.
How to render this component and pass children prop ?
This is how component looks like
import React, { FC } from "react";
import { ReusableToolbarWrapper, StyledText } from "./styles";
import CloseIcon from "#material-ui/icons/Close";
export interface ReusableToolbarProps {
childred: string;
}
export const ReusableToolbar: FC<ReusableToolbarProps> = (children) => {
return (
<ReusableToolbarWrapper>
<CloseIcon
style={{
width: "14px",
height: "14px",
marginLeft: "20px",
marginTop: "22px",
marginBottom: "24px",
}}
/>
<StyledText>{children}</StyledText>
</ReusableToolbarWrapper>
);
};
And how should I render it here : ?
import {Toolbar} from "./toolbar";
import {ReusableToolbar} from "./ReusableToolbar/ReusableToolbar.tsx";
function App() {
return (
<ReusableToolbar></ReusableToolbar>
// <Toolbar/>
);
}
export default App;
thanks
children is the prop that goes inside the tags like this
import {Toolbar} from "./toolbar";
import {ReusableToolbar} from "./ReusableToolbar/ReusableToolbar.tsx";
function App() {
return (
<ReusableToolbar>Hello world</ReusableToolbar>
// <Toolbar/>
);
}
export default App;
you can alternatively pass it as a regular prop like this
import {Toolbar} from "./toolbar";
import {ReusableToolbar} from "./ReusableToolbar/ReusableToolbar.tsx";
function App() {
return (
<ReusableToolbar children="Hello world" />
// <Toolbar/>
);
}
export default App;
First, in the App component you should replace (children) by ({children}) or (props) and then use props.children
export interface ReusableToolbarProps {
children: string;
}
export const ReusableToolbar: FC<ReusableToolbarProps> = ({children}) => {
return (
<ReusableToolbarWrapper>
...
<StyledText>{children}</StyledText>
</ReusableToolbarWrapper>
);
};
Then you can write the text to display inside the <ReusableToolbar> markup (everything inside the tag <ReusableToolbar> is passed to the component children property)
<ReusableToolbar>Some text to display</ReusableToolbar>

How can I update state in two separate components using a custom hook?

I am trying to create a custom hook to be able to open and close a pop-out menu with conditional rendering using style display: none and display:block. I think I understand how to share the state between the components (I can console log that and get that working) , but I can not figure out how to update the state using the hook.
I am certain that I have some fundamental misunderstanding here but if anyone can clarify what it is I am trying to achieve that would be awesome! I have tried to learn this for several nights and here is where I have got to.
This is the header of the pop out menu it only contains a close button at the moment
import React from 'react'
import { FontAwesomeIcon } from '#fortawesome/react-fontawesome'
import { faWindowClose } from '#fortawesome/free-solid-svg-icons'
import useOpenCloseElementMenu from '../Hooks/openCloseElementMenu'
function ElementMenuHeader() {
const { elementMenuOpenClose, setElementMenuOpenClose } = useOpenCloseElementMenu();
return (
<div id="App-Close-Element-Menu-Container">
<button id="App-Close-Element-Menu"
onClick={() => setElementMenuOpenClose(false) }
>
<FontAwesomeIcon icon={faWindowClose} />
</button>
</div>
);
}
export default ElementMenuHeader
This is the pop out menu
import React from 'react';
import SizerGroup from '../Sizer/sizerGroup';
import './element-menu.css';
import ElementMenuHeader from './element-menu-header';
import TitleWithLine from './title-with-line';
import TypeSelector from './type-selector';
import TemplateSelector from './template-selector';
import useOpenCloseElementMenu from '../Hooks/openCloseElementMenu'
function Editor(props) {
const { elementMenuOpenClose, setElementMenuOpenClose } = useOpenCloseElementMenu();
console.log(elementMenuOpenClose);
return (
<div className="App-Element-Menu"
style={{display: elementMenuOpenClose ? 'block' : 'none' }}
>
<ElementMenuHeader />
<TitleWithLine title="Element size" />
<SizerGroup />
<TitleWithLine title="Elements" />
<TypeSelector />
<TitleWithLine title="Templates" />
<TemplateSelector />
</div>
);
}
export default Editor
This is the toolbar that has the open menu button
import React from 'react'
import Button from '../Button/button'
import { FontAwesomeIcon } from '#fortawesome/react-fontawesome'
import { faBoxes } from '#fortawesome/free-solid-svg-icons'
import './toolbar.css'
import useOpenCloseElementMenu from '../Hooks/openCloseElementMenu'
function Toolbar(props) {
const { toolbar_show_or_hide } = props
const elementMenuIcon = <FontAwesomeIcon icon={ faBoxes } />
const { elementMenuOpenClose, setElementMenuOpenClose } = useOpenCloseElementMenu();
const openEditor = setElementMenuOpenClose[true]
return (
<div className="App-Toolbar" style={{ display: toolbar_show_or_hide ? "flex" : "none" }} >
<Button
id="App-Open-Element-Menu-Button"
icon={ elementMenuIcon }
useToolTip={ true }
toolTipText="Elements menu. Select elements to populate the theme."
buttonFunction={ openEditor }
/>
</div>
)
}
export default Toolbar
This is the hook
import React, { useState } from 'react';
const useOpenCloseElementMenu = () => {
const [elementMenuOpenClose, setElementMenuOpenClose] = useState(false);
return { elementMenuOpenClose, setElementMenuOpenClose };
};
export default useOpenCloseElementMenu;
I feel you donot have to pass the hook in a separate function, useOpenCloseElementMenu, like you did.
Instead of importing the ../Hooks/openCloseElementMenu function thing,
I'd rather just call the hooks directly instead as
const [elementMenuOpenClose, setElementMenuOpenClose] = useState(false);
in the editor and toolbar component in place of const { elementMenuOpenClose, setElementMenuOpenClose } = useOpenCloseElementMenu();.
Also which component did you use the toolbar component if I may ask? Because I don't seem to see any of that here...It confusing where it got the toolbar_show... props from.
{I hope this helps cause that seem like the most obvious reason}

Resources