How to dynamically import icon / jsx element by string? - javascript

I'm working on an icon selector at the moment. Once an icon is chosen, the selector returns the icon as a string, like AirBalloon. Then, I want to display that icon on my page, so I need to import it (I'm using https://www.npmjs.com/package/tabler-icons-react).
Normally, I would do it like import {AirBallon} from 'tabler-icons-react';.
So I tried this:
<IconSelector
active={iconSelectorActive}
setIcon={setIcon}
additionalEvent={async () => {
console.log(icon); // Logs the icon I selected
setActiveIcon((await import('../../node_modules/tabler-icons-react/dist/icons/' + icon)));
}}
></IconSelector>
But if I try to embed it into my JSX like that:
<Button
onClick={() =>
setIconSelectorActive(!iconSelectorActive)
}
variant="PRIMARY"
>
{activeIcon}
</Button>
It throws the error Error: Cannot find module './' when I click an item from the selector.
How do I fix this?

If you want to keep the dynamic import, you can use await import('tabler-icons-react') then select the icon you need from the default export dynamically.
<IconSelector
active={iconSelectorActive}
setIcon={setIcon}
additionalEvent={async () => {
console.log(icon); // Logs the selected icon
const icons = await import('tabler-icons-react');
const IconComponent = icons[icon];
setActiveIcon(<IconComponent />);
}}
></IconSelector>

First, you need to import all the icons from the library like this:
import * as allIcons from "tabler-icons-react";
And then retrieve the selected icon by using its name like this:
const iconNameToBeUsed = "Activity";
const IconToBeUsed = allIcons[iconNameToBeUsed];
Finally render the selected icon jsx like this:
<IconToBeUsed />
You can take a look at this sandbox for a live working example of this solution.

Related

Is there a way to get all classes using document.querySelectorAll in reactjs and manipulate it?

I wanted to allow users to change the theme of the application by picking which theme they want the body's background color changes and all button colors. But the problem is that whenever I use document.querySelectorAll('.btn-theme-1').style.backgroundColor it tells me that it cannot read those properties.
I know of useRef() but in my case I am trying to select all buttons throughout the entire application. Not just one element in the current component. So I would like to know if there is a way to fix what I am attempting or if I am doing this the wrong way.
Here is the code for what I tried. This is my pick theme component:
import ColorThemes from '../data/ColorThemes';
import { useEffect } from 'react';
const PickTheme = () => {
const changeTheme = (c) => {
document.body.style.background = c.default || c.bgColor;
document.body.style.color = c.bodyColor;
document.querySelector('.bi-quote').style.color = c.buttonBg;
document.querySelectorAll('.text-color').forEach(el => el.style.color = c.fontColor)
document.querySelectorAll('.btn-theme-1').forEach(el => {
el.style.color = c.buttonColor;
el.style.backgroundColor = c.buttonBg;
});
};
useEffect(() => {
},[changeTheme]);
return(
ColorThemes.background.map(c => {
if(c.bgColor) {
return(
<button type="button" key={c.bgColor} className="btn btn-light me-2 p-3 rounded-5" onClick={() => changeTheme(c)} style={{backgroundColor: c.bgColor}}></button>
);
} else {
return(
<><br/><button type="button" key={c.default} className="btn btn-light me-2 mt-2 rounded-5" onClick={() => changeTheme(c)}>Default</button></>
);
}
})
);
};
export default PickTheme;
It successfully changes the bodys color and background color but not the other classes. I tried with and without useEffect and still receive the same issue.
If I comment out everything except the last selector, the buttons then change colors. So maybe it is conflicting or cannot change everything at once, for example:
const changeTheme = (c) => {
// document.body.style.background = c.default || c.bgColor;
// document.body.style.color = c.bodyColor;
// document.querySelector('.bi-quote').style.color = c.buttonBg;
// document.querySelectorAll('.text-color').forEach(el => el.style.color = c.fontColor)
document.querySelectorAll('.btn-theme-1').forEach(el => {
el.style.color = c.buttonColor;
el.style.backgroundColor = c.buttonBg;
});
};
This changes the buttons background and color after commenting out the other parts.
I know of useRef() but in my case I am trying to select all buttons throughout the entire application. Not just one element in the current component.
Using .querySelector or any other selector will select only those elements that are currently rendered, actually. So if you e.g. toggle the state and component re-renders with different elements, they will not be affected with your change, which will result in partially toggled theme for different elements.
You should either set a context, wrapping whole App or set a redux variable holding info which theme is currently selected. Then, you will be able to manipulate styles using e.g. theme in styled components: https://styled-components.com/docs/advanced#theming or just toggling classNames with css modules, basing on that variable.
You can use useRef() with a function that runs on each element and add them to it, let me dive deeper into it.
Let's first create a reference containing, for now, an empty array:
const myRef = useRef([])
Great, we now want to populate that.
It's going to be in two parts, first, let's make a function that will populates that array:
const addToMyRef = (element) => {
if (element && !myRef.current.includes(element)) {
myRef.current.push(element);
}
};`
Great, we now have a function that takes an element of the DOM as an argument, verifies that it exists and that it is not yet in our array, then adds it.
But now, when will it get triggered? Simply in the ref= attribute!
<button ref={addToMyRef}></button>
You'll now see that your reference is now a set of them, so you can create a reference per element, or maybe modify the code a little to makes it takes objects to have a all-in-one reference for each element of the dom. (We could imagine it being myRef.buttons/myRef.inputs...)

How to add text on border with css for input field using React JS?

I want to create an input box like below,
using css, however am not able to find anything on how to do the text on border for input fields anywhere at all
I tried using but am unable to create the input box as shown above.
I am very much stuck here, it would be a great help if anyone could suggest anyway to create an input field like shown above.
Thank you in advance.
You can use react-native-material-textfield library and need some customization. There is a prop renderLeftAccessory which can render your left view like country code here. Try example code here
import React, { Component } from 'react';
import {
TextField,
FilledTextField,
OutlinedTextField,
} from 'react-native-material-textfield';
class Example extends Component {
fieldRef = React.createRef();
onSubmit = () => {
let { current: field } = this.fieldRef;
console.log(field.value());
};
formatText = (text) => {
return text.replace(/[^+\d]/g, '');
};
render() {
return (
<OutlinedTextField
label='Phone number'
keyboardType='phone-pad'
formatText={this.formatText}
onSubmitEditing={this.onSubmit}
ref={this.fieldRef}
/>
);
}
}

React add html attribute to external component

I am using a component that I cannot change directly, but I would like to extend.
import { Button } from '#external-library'
// Currently how the button component is being used
<Button click={() => doSomething()} />
// I would like to add a tabIndex to the button
<Button click={() => doSomething()} tabIndex={0} />
I cannot add an attribute because the component is not expecting a tabIndex. I cannot directly modify the Button component.
How can I extend the <Button /> component so I can add attributes like tabIndex, etc?
I was hoping something like the following would work:
export default class ExtendedButton extends Button { }
// except I'm dealing with functional components
You can't edit custom component implementation without changing its internals.
// You can't add tabIndex to internal button without changing its implementation
const Button = () => <button>Click</button>;
In such cases, you implement a wrapper with desired props:
const Component = () => {
return (
<div tabIndex={0}>
<Button />
</div>
);
};
If the component forwarding ref (also depends to which element it forwarded in the implementation), you can use its attributes:
// Assumption that Button component forwards ref
const Button = React.forwardRef((props,ref) => <button ref={ref}>Click</button>);
<Button ref={myRef}/>
// Usage
myRef.current.tabIndex = 0;
You can access the inner DOM button element using React refs(read here)
most likely the external-lib you use provide a ref prop for the Button component which you use to pass your own create ref
const buttonRef = useRef(null);
<Button ref={buttonRef}/>
Then you can use buttonRef.current to add tabIndex when your data is ready to be populated in like
useEffect( () => {
if(buttonRef && buttonRef.current){
buttonRef.current.tabIndex = 2;
}
}, [props.someProperty] );

how to insert data in React js

I am facing an issue with React JS , i want to do show all the data in a row not in a column.
What should I change in my code?
Working demo:
https://codesandbox.io/s/nostalgic-wood-9jrib?file=/src/App.js:0-355
react component
import React from "react";
export default function App() {
const names =
data &&
data.length &&
data.map(({ full_name }) => <p key={full_name}>{full_name}</p>);
return <div className="row">{names}</div>;
}
p : is being used as paragraph so it will cover whole width by default, until unless you override with css
You can use,
// if you need more space you can apply class to span and add some padding-mrgin
// as per your need
<span key={full_name}>{full_name} {' '}</span>
in place of
<p key={full_name}>{full_name}</p>

Setting aria-label/aria-labelledby within React.cloneElement()?

Basically, I'm trying to clone an element and change its aria-label within React.cloneElement. I've got a component - ButtonArrows - that creates two Button components, one with an arrow icon pointing left, and one pointing right. I'd like to be able to programmatically change the aria-label, but the hyphen throws an error.
Here's some code showing what I'm trying to do:
const ButtonArrows = ({leftArrow, rightArrow, ...props})
const prevButton = (
<Button
aria-label="Previous",
icon={leftArrow}
/>
);
const nextButton = React.cloneElement(prevButton, {
//this is where the problem is:
aria-label="Next",
icon={rightArrow}
});
return(<div {...props}>{prevButton}{nextButton}</div>);
}
and obviously I can't do aria-label="Next" because of the hyphen.
Any suggestions? React unfortunately doesn't have anything like htmlFor (to stand in for html-for) when it comest to aria labels. Should I just put an ariaLabel prop on Button and pass it down, or is there a way to do it directly with cloneElement that I'm missing?
You should be able to use a plain JavaScript object here:
const nextButton = React.cloneElement(prevButton, {
'aria-label': 'Next',
icon: rightArrow
});
const ButtonArrows = ({leftArrow, rightArrow, ...props})
const prevButton = (
<Button
ariaLabel="Previous",
icon={leftArrow}
/>
);
const nextButton = React.cloneElement(prevButton, {
//this is where the problem is:
ariaLabelledby="Next",
icon={rightArrow}
});
return(<div {...props}>{prevButton}{nextButton}</div>);
}
change aria-label to ariaLabel

Categories

Resources