Import SVG in parent and pass to child - javascript

Currently I import Icons like this
//index.ts file for svgs
import BackArrow from './back-arrow.svg'
export {
BackArrow,
...
}
In a component i can now import and use it easily
import { BackArrow } from '../../assets/images'
....
return <BackArrow />
Now I tried to make a (child) component which should display two buttons and a text, which are passed by the parent component.
Parent uses the child like this:
<Button
label='hi'
iconLeft={BackIcon}
buttonTheme={BUTTONS_THEME.dark}
/>
Child looks like:
//Styled components (ThemedButton, Icon, Label)
const Button = ({ label, iconLeft, iconRight, theme}) => {
<ThemedButton>
{iconLeft && <Icon theme={theme} xml={iconLeft} />}
<Label theme={theme}>{label}</Label>
{iconRight && <Icon theme={theme} xml={iconRight} />}
</ThemedButton>
and this all works fine, if in the parent component I import the svg like this:
import BackArrow from '../../assets/images/back-arrow.svg'
but i want to use the "normal" way to import it as I do everywhere else in the App, like this: import { BackArrow } from '../../assets/images'
Anyone any idea how to solve this? I tried to pass the Icons like this <Button leftIcon={<BackArrow />}... />, but I can't get this working, as I don't know how I can use and style the icon in the child.

I think what you are trying to do is use your BackArrow in your child as <BackArrow />
The below approach is for a CRA app where you can import svg as a ReactComponent
I solved a similar issue by following this approach.
import is as
import { BackArrow } from './back-arrow.svg
Continue to pass it to child as
<Button
label='hi'
iconLeft={BackIcon}
buttonTheme={BUTTONS_THEME.dark}
/>
but, now in child do not destructure your props,
const Button = (props) => {
<ThemedButton>
{props.iconLeft && <props.iconLeft ... />}
<Label theme={props.theme}>{props.label}</Label>
{props.iconRight && <props.iconRight ... />}
</ThemedButton>
EDIT:
In the child component when you have <props.iconLeft ... /> then this is equivalent to <BackArrow /> so now you are free to style it, essentially treat it the way you would in the parent when you had <BackArrow />. Destructuring your props in the child doesn't quite work here.

I don't know what you are using for your build processes. but it's probably because of loaders that you use for loading svg. If you import your svg like this: import BackArrow from '../../assets/images/back-arrow.svg' it's probably load it as dataURL. And if you import your svg like this: import { BackArrow } from '../../assets/images' it imports as react component. Thus you can not use this as xml props of a Icon.

Related

How can I call the function of a parent component inside of a child component?

First off, I'm newer to react so please feel free to critique any sort of architectural problems I have, I feel like there's a better way to write this but I've been struggling for an hour trying to get it to work.
Parent Element (TileGrid):
import React, { Component } from 'react'
import Tile from './Tile.js';
export default class TileGrid extends Component {
constructor(props) {
super(props);
}
updateTileData(value) {
{/* Do something with value here */}
}
render() {
this.test()
return (
{/* The Tile component is a custom button with some richer features. */}
<Tile name='Dog' image='dog' />
)
}
}
Child Element (Tile):
import React from 'react';
import ButtonBase from '#mui/material/ButtonBase';
import './styles/Tile.css';
function Tile(props) {
return(
<div className="Tile">
<ButtonBase onClick={/* This is where the Tile needs to call updateTileData in TileGrid */}>
Button
</ButtonBase>
</div>
);
}
export default Tile;
So there's a function inside of TileGrid called updateTileData that is going to take some data and use it to update the state of TileGrid. The Tile component exists as a child within the TileGrid component. I've tried all sorts of stuff on here but this seems like a simple task to do, is there a better way to write this functionality? I'm not tied down to anything in the code, please tell me if there's a better way to do this. I'm still learning this framework and this issue has me hung up. Thanks, Archer.
pass the function from parent to child as a prop example :
<Child func={this.functionInParent} />
props.func() //called inside child
you need to pass a prop to child Element and Call it.
import React, { Component } from 'react'
import Tile from './Tile.js';
export default class TileGrid extends Component {
updateTileData(value) {
{/* Do something with value here */}
}
render() {
this.test()
return (
{/* The Tile component is a custom button with some richer features. */}
<Tile name='Dog' image='dog' setValue={this.updateTileData} />
)
}
}
child Element :
import React from 'react';
import ButtonBase from '#mui/material/ButtonBase';
import './styles/Tile.css';
function Tile(props) {
return(
<div className="Tile">
<ButtonBase onClick={()=>props.setValue("your value")}>
Button
</ButtonBase>
</div>
);
}
export default Tile;

React-Tooltip not displaying until page is refreshed

Bit of a weird one here. My website is virtually done, there is just one issue. I've implemented tooltips, but they only display once I refresh the page! Here is a GIF reproducing the issue:
https://i.imgur.com/NbHyN77.mp4
The package is from NPM, at the following link: https://www.npmjs.com/package/react-tooltip
I've went through their documentation, troubleshooting and issues reported on their github repo, but there is nothing describing my issue. The site is live at: https://ezekias1337.github.io/Burning-Crusade-Talent-Calculator/#/
Oddly enough, if I bookmark one of the routes and load it in a fresh tab, it loads the first time. The issue only happens when I select the component from my Icons.
I made sure to import ReactTooltip from "react-tooltip"; I also added at the bottom of each component, and in app.js. Adding the data-for attribute hasn't fixed the issue.
Here is the code of my App.js:
import ReactTooltip from 'react-tooltip';
class App extends Component {
render() {
return (
<div className="App">
<CustomNavbar />
<ClassSelector />
<FooterComponent />
<ReactTooltip
html={true}
/>
</div>
);
}
}
export default App;
Here is the code relevant to tooltips in each individual component:
a.) The image that has the tooltip (every image has unique tooltip)
<img
onMouseEnter={this.displayMouseOverlayInnerElement}
onMouseLeave={this.hideMouseOverlayInnerElement}
onMouseDown={() => {
this.talentClick();
this.toolTipFunction();
}}
onTouchEnd={this.talentClick}
className="talentHover"
src={overlayImage}
style={{ display: "none" }}
data-tip={Hunter[0].toolTip[0]}
id="1"
/>
b.) The bottom of the component
<ReactTooltip data-html="true" />
Any idea what I can do to fix this?
In case anyone else is having this issue, I have finally found the solution after hours of pulling my hair out.
I used the following function:
rebuildToolTip(){
ReactTooltip.rebuild();
}
Subsequently I added this function as an event handler for onLoad on the component being rendered.
<div
style={{ position: "relative" }}
onContextMenu={(e) => e.preventDefault()}
className="frame-wrapper"
id="Hunter"
onLoad={() => {
this.scrollComponentIntoView();
this.rebuildToolTip();
}}
>
Here is something that worked for me (https://github.com/wwayne/react-tooltip/issues/268), create a new component for tool tip and pass required details in props as below code.
import React from "react";
import ReactDOM from "react-dom";
import ReactTooltip from "react-tooltip";
// Create root level element for react-tooltips
const domNode = document.createElement('div');
document.body.appendChild(domNode);
// Wrapper component to portal react-tooltips
function BodyPortal ({ children }) {
return ReactDOM.createPortal(
children,
domNode
);
}
// Custom tooltip wrapper to ensure all tooltips get rendered into the portal
function CustomReactTooltip (props) {
return (
<BodyPortal>
<ReactTooltip
type="light"
effect="solid"
delayHide={50}
{...props}
/>
</BodyPortal>
);
}
export default CustomReactTooltip;

How to dynamically import a component in React?

I am trying to optimize a component of mine with dynamic rendering, but I'm facing some issues, so this is my current situation:
I have an Alert component that renders an alert message, along with an icon.
I have a Icons module, which is a library of icons
I am currently rendering the icon as follows (this is not actual code, it's just to give the idea):
import * as icons from '../icons';
import DefaultIcon from '../icons';
function Alert(iconName='defaultIcon', message) {
function getIcon(iconName) {
if (iconName === 'defaultIcon') {
return DefaultIcon()
}
return icons[iconName]
}
return (
<div>
<span>{getIcon(iconName)}</span>
<span>{message}</span>
</div>
)
}
So, suppose Alert gets called without iconName most of the times, so the components doesn't need to import all of the icons at all.
I would like to avoid including all of the icons in the Alert component by default, but only doing so if iconName is specified
Is it even possible to do so?
I don't think it's possible this way.
Maybe create a component for the Icon that imports the icon libary. In the Alert component you could implement the Icon component as a child:
<Alert message="Alert!">
<Icon iconName="defaultIcon" />
</Alert>
You should import icons dynamically with type or name etc. something like below.
import React from 'react';
export default function Icon({ type, ...props }) {
const Icon = require(`./icons/${type}`).default;
return <Icon {...props} />
}
import Icon from './Icon';
<Icon type="addIcon" />
Ok, looks like I managed, and that's how I did it:
import DefaultIcon from '../icons';
function Alert(message, iconName="") {
const [icon, useIcon] = useState();
//componentDidMount
const useEffect(() => {
//if an icon name is specified, import the icons
if (iconName) {
import("./icons").then(icons => setIcon(icons[iconName]))
} else {
setIcon(DefaultIcon)
}
}
,[])
return (
<span>{icon}</span>
<span>{message}</span>
)
}

ReactJS: Two different styles for one component

I have a component that I use twice in the same code, it looks like this:
import React from 'react';
import Container from 'Base/Grid/Container';
import styles from './index.css';
const Columns = props => <Container {...props} className={styles.root} block/>;
export default Columns;
How can i, when importing, apply another style class to the second used Columns?
thanks in advance
You can define another style beside of your styles.root that is passed from the props. Like below:
const Columns = props => <Container {...props} className={[styles.root,props.newStyles]} block/>;
So when you make a Columns component you can pass the specific styles. For example:
<Columns newStyles={{color: 'red'}} />
So you can customize the style for each component which you use.
or if you don't want to use the root style you can make it conditional that if there the newStyle was passed use it. If not just use the styles.root. And the code would be like this:
Columns = props => <Container {...props} className={props.newStyle || styles.root} block/>;
A clean solution is to use react composition. Declare the "base" component in one file, and then export in two different files with two different names the styled one.
// BaseComponent.jsx
export default Column = () => <div>Column</div>;
// RedColumn.jsx
import Column from './Column';
const RedColumn = () => <Column style={{color: "red"}} />;
export default RedColumn;
// BlueColumn.jsx
import Column from './Column';
const BlueColumn = () => <Column style={{color: "blue"}} />;
export default BlueColumn;

Material UI How to set external styles for sub-components with one main common style for the entire component

I have a page made up of many sub-components. The page folder holds the page file, external styles file and each sub-components folders - which also consists of their own style.
I'm not sure how to set up an external shared common style file for all sub-components along with external styles for each of the sub-components.
Ie. DemoStyles.js is the common styles and Demo.js is where are the sub-components are called.
Demo.js:
import React from "react";
import PropTypes from "prop-types";
import { withStyles } from "#material-ui/core/styles";
import styles from "./DemoStyles";
import Red from "./red/Red";
import Blue from "./blue/Blue";
function SharedStyles(props) {
return (
<React.Fragment>
<Red />
<Blue />
</React.Fragment>
);
}
SharedStyles.propTypes = {
classes: PropTypes.object.isRequired
};
export default withStyles(styles)(SharedStyles);
DemoStyles.js:
export default theme => ({
title: {
color: "white",
fontSize: 30
}
});
The title style is not being applied.
The className is set in the Red.js file:
TL;DR:
I need one common external style file to apply to all subcomponents living in one folder; and each subcomponent needs their own external style specific to it.
Here is the code sample: https://codesandbox.io/s/rypoqk07lo
SOLVED:
I solved this by importing the demoStyles into the RedStyles.js file and then calling it with the spread operator and passing theme as an argument like so:
RedStyles.js
import demoStyles from "../DemoStyles";
export default theme => ({
...demoStyles(theme),
red: {
backgroundColor: "red"
}
});
code sample updated as well
You forgot to pass the classes props on Demo.js to your components like you do on <Red /> and <Blue />.
const { classes } = props;
return (
<React.Fragment>
<Red classes={classes} />
<Blue classes={classes} />
</React.Fragment>
);
Its good to remember that Material-UI has themes support. It's better to use it on Demo.js depending of what you trying to do.

Categories