React - How to specify an icon in an object as a value - javascript

I have an array with objects before, I applied icons from fontAwesome as a value, it looked like this
const SideBarColors = [
{
IconStyle: {
Icon: faDotCircle,
}
}
]
Then in another component I got this icon like this
<FontAwesomeIcon icon={SideBarStyle.Icon} />
Now I use a different approach (React Icons), the point is that now I create icons in this way
<BsBraces />
In this case, I cannot assign this icon inside the object as a value, it gives an error
IconStyle: {
IconTitle: <BsBraces />
}
What should I do in this case?

You just need to :
IconStyle: {
IconTitle: BsBraces
}
without the </>
Apply it to JSX like:
<IconStyles.IconTitle />

Related

React Native - NativeBase extendTheme Custom Components not Loading Initially

I have a custom component for Buttons using NativeBase, and it seems work great initially; once I restart the project, the default style for the button I have set no longer works. To fix this, I have to manually set a variation, and then remove it, like so :
<Button variation="random" /> // Sets the variation to something random
<Button /> // delete the variation section of button, and now the default loads
I have it set up like so:
export const style = extendTheme({
components:{
Button: {
baseStyle: {
rounded: 'lg',
},
defaultProps: {
size: 'sm',
colorScheme: 'green',
_text: {
color: 'white',
fontSize: '30px',
...fonts.h1, //custom font
margin: -2,
}
},
}
}
Then, within App.js:
const style = style // from import
return (
<NativeBaseProvider theme={style}>
<NavigationContainer>
<StackNavigator>
...etc.
)
And finally, in another file:
<Button />
Why is this occurring? I assume it has something to do with NativeBaseProvider wrapping everything in App.js, instead of just wrapping each individual file using the custom components with it.
Thanks!

Passing string from parent to render multiple children

I may lose something on the way.. I want to pass object arguments to a children to dinamically render it in three different ways.
Here is my object:
const cards = [
{
imgSrc: "first",
desc: "some text"
},
{
imgSrc: "second",
desc: "some text"
},
{
imgSrc: "third",
desc: "some text"
}
];
This is the children component:
import { Box } from '#mui/system'
import React from 'react'
import "../../style/main.scss"
import first from "../../images/veloce.png"
import second from "../../images/sicuro.png"
import third from "../../images/vicino.png"
import { Typography } from '#mui/material'
function Card( source, text ) {
return (
<Box className="foundation-card">
<img src={source}/>
<Typography variant="h6">{text}</Typography>
<Typography variant="body2">{text}</Typography>
</Box>
)
}
export default Card
And then i have the parent component where i want to render multiple Card mapping the cards array:
import Card from "./Card"
import CustomDivider from "../foundation/CustomDivider"
function Values() {
return (
<Box className="vertical-box main-maxw section-margin">
<Typography variant="h4">Perchè sceglierci</Typography>
<CustomDivider />
<Box className="foundation-box values">
{cards.map((p) => {
return <Card source={p.imgSrc} text={p.desc} />
})}
</Box>
</Box>
)
}
export default Values
and then i receive this:
Error: Objects are not valid as a React child (found: object with keys {}). If you meant to render a collection of children, use an array instead.
I suppose it is a stupid error but i am in my first approach and i don't know how to move.
Thank you all.
I think the problem is that your card function is expecting positional arguments but you're calling it with an object.
<Card source={p.imgSrc} text={p.desc} />
// These two things are equivalent.
Card({source: p.imgSrc, text: p.desc})
So essentially you were assigning the source arg and object that contained both source and text.
Try changing your card function to accept an object
function Card({source, text}) {
...
}
I see some strange things.
1- where you are mapping the cards, you are not importing the cards array, or i cant see where are you getting the cards array from
2- function card is a react component so you should wrap the props with {} as this: function Card({source,text})

Passing props to components rendered dynamically from object

I have an object full of components:
const Icons = {
// commonly used darksky icon names
"clear-day": <ClearDayIcon />,
"clear-night": <ClearNightIcon />,
"rain": <RainMediumIcon />,
"snow": <SnowIcon />,
"sleet": <RainHeavyIcon />,
"wind": <WindyDayIcon />,
// etc..
}
Each component is really just an svg wrapped as a react component. But I need it to receive props from the place where its called, especially the className prop.
What I want to be able to do is call these components from my other components in the following manner:
<WeatherIcon icon={icon} className="some-class" />
Where the icon prop will determine which icon component is chosen. So I tried this:
const WeatherIcon = props => Icons[ props.icon ]
So this works partially, as I can write <WeatherIcon icon={'clear-night'} />, and the correct component is rendered. However, there's no way to pass any other props from my WeatherIcon component down through each Icon. For example, writing <WeatherIcon icon={'clear-night'} className="some-class" /> clearly does not pass the className prop (or any other prop) down through to each individual component. I tried doing something like this:
const Icons = {
"clear-day": props => <ClearDayIcon {...props} />,
"clear-night": props => <ClearNightIcon {...props} />,
// etc..
}
But this doesn't work because now I'm returning a Component rather than a <Component />. I saw the solutions in the question Passing props to dynamically loaded components, but these all suggest calling the component like { Icons['clear-day'](className: 'some-class', anotherProp: 'some-prop') }. I feel like this is not very elegant. There must be a way to write it as a <WeatherIcon icon={'some-icon'} className={'some-class'} someProp={'some-prop'} />, and have the props filter down correctly. (I do realize that all the props would filter all the way down to my SVG component - that's fine). I feel like theres a higher order component waiting to be written here, but right now its eluding me.
Thanks for reading
I'm not 100% sure about if this will fulfill your requirement but I would probably try something like this;
const Icons = {
"clear-day": ClearDayIcon,
"clear-night": ClearNightIcon,
"rain": RainMediumIcon,
"snow": SnowIcon,
"sleet": RainHeavyIcon,
"wind": WindyDayIcon,
// etc..
}
const Icon = ({icon, ...rest}) => {
const IconComponent = Icons[icon]
if(!IconComponent) {
// Or throw an exception maybe.
// At least print some console warnings in development env.
return null;
}
return <IconComponent {...rest} />
}
With this way, you can select one of your Icon components and pass any prop to it.
And when you want to use it, you can use it like this;
// ...
<Icon icon="clear-day" className="some-class" />
// ...
I figured out that this works too - write the object containing the components as a function of props, which returns the object. Then you can use a {...props} in each component:
const Icons = props => ({
"clear-day": <ClearDayIcon {...props} />,
"clear-night": <ClearNightIcon {...props} />,
"rain": <RainMediumIcon {...props} />,
etc.
})
Then the wrapper component looks like this:
const WeatherIcon = props => Icons(props)[ props.icon ]
Pretty simple - I knew I was close. I'm not sure if this is considered a higher order component (HOC). It is a component that returns another component based on its props, with new props attached. Does that count as an HOC?

How to group props and make code more readable?

Is this the only way of defining properties?
<YAxis
id="axis1"
label="Requests"
style={{ labelColor: scheme.requests }}
labelOffset={-10}
min={0}
max={1000}
format=",.0f"
width="60"
type="linear"
/>
This can be really cluttered if there is a long list of properties
It remembers me a bit of inline css, which can become hard to read.
can't we just say:
var yAxis_props = ( id = ... label = ... )
and then insert them like:
<YAxis yAxis_props />
Yes can do that, define yAxis_props as an object with all the key-values. Like this:
var yAxis_props = {
id: '',
label: ''
}
Then using spread operator pass all the values as a separate prop, like this:
<YAxis
{...yAxis_props}
/>
Check React Doc.
Ok, you are getting confused between these two ways:
// first way
<Component id='', label='' />
// second way
const obj = { id:'', label: ''}
<Component {...obj} />
Behind the scene both are same. Check this Babel repl output of these two ways. We write jsx, which will be converted into React.createElemet(component/html tag, props, children).
Code:
function ComponentA () {
return( <div>Hello</div> )
}
function ComponentB() {
return (
<div>
<A id='a' label='b' />
<A {...{id:'a', label:'b'}} />
</div>
)
}
Converted form:
function ComponentA() {
return react("div", null, "Hello");
}
function ComponentB() {
return react(
"div",
null,
react(A, {
id: "a",
label: "b"
}),
react(A, {
id: "a",
label: "b"
})
);
}
You can achieve something similar using spread operator.
let props={a:1, b:2}
...
<MyComponent {...props}/>
While the answers here cover the spread approach which is valid and works great, I would like remind that having too many props for a single component might indicate that this component suffers from too many variations, which in turn makes it really hard to maintain and extend.
In such cases you would like to consider the approach of Render Props or "children as function" as it is sometimes called.
This approach allows you to keep your component logic and basic behavior in a single place, while the developers using it can use it in composition to alter the way the component renders and interacts with the surroundings.
There is a great talk by Kent C. Dodds about this here.
Hope this helps :)

Change paper color Material-UI

I'm developing a React project using the material-ui library. I'm currently trying to add a drawer which is working fine for me. However, I'm trying to change the background color of this drawer. I've heard that the way to do this is by changing the color of the drawer's paper. I've tried adding the following tag to my CSS object:
const styles = theme => ({
background:"BLUE"
Then I reference this object in my render function using the classNames library:
render(){
const { classes } = this.props;
return(
<div className={styles.root}>
<CssBaseline />
<Drawer
variant="permanent"
className={classNames(classes.drawer, {
[classes.drawerOpen]: this.state.open,
[classes.drawerClose]: !this.state.open
})}
classes = {{
paper: classNames({
background:classes.background,
[classes.drawerOpen]: this.state.open,
[classes.drawerClose]: !this.state.open
})
}}
However, when I run this on localhost, the paper is still a plain old white. Am I missing something about the classNames library or is a special case of the paper tag? Thanks in advance and let me know if I should supply more info than this.
You have a couple issues in the code shown in your question.
For your styles, you need something more like the following:
const styles = theme => ({
drawerPaper: { background: "blue" }
});
In this case, "drawerPaper" is the key for my class name and then the object to the right contains the CSS properties for that class. When passed into withStyles, this will generate CSS like the following:
<style>
.classname-generated-for-drawerPaper-key: {
background: blue;
}
</style>
You had a class name key of "background" with the string "BLUE" as the CSS properties which will end up with CSS like the following:
<style>
.classname-generated-for-background-key: {
0: B;
1: L;
2: U;
3: E;
}
</style>
which of course is not valid CSS and will have no effect on the paper.
The second issue is in how you specify the classes:
classes = {{
paper: classNames({
background:classes.background,
[classes.drawerOpen]: this.state.open,
[classes.drawerClose]: !this.state.open
})
}}
When you pass an object to classNames, the keys of the object are the class names and the associated value controls (based on whether it is falsey or truthy) whether the class name should be included. With the syntax you used, classes.background will always be truthy which means that the class "background" (rather than the generated class name in classes.background) will be included which will have no effect since a "background" class hasn't been defined.
Instead you should have:
classes = {{
paper: classNames(classes.drawerPaper, {
[classes.drawerOpen]: this.state.open,
[classes.drawerClose]: !this.state.open
})
}}
which will unconditionally include classes.drawerPaper.
Here is a modified version of one of the Drawer demos, but with the background color of the drawer changed to blue: https://codesandbox.io/s/wqlwyk7p4l
If you're using global theme = createTheme( the color of a background paper can be set as following
const theme = createTheme({
palette: {
{
primary: colors.blue,
background: {
default: colors.grey[50],
paper: colors.common.white,
},
// ...

Categories

Resources