Conditionally render border colour React Native - javascript

I'm trying to conditionally change my border color based on whether the textInput is under focus or not.
<View
style={focus? { ...styles.inputRow, ...bgColorOnFocus }: { ...styles.inputRow, ...bgColorOnBlur }}>
This works as expected, but I'm having to repeat inputRow styles twice. I've been trying to improve the code with the following code where I add the object based on whether it's on focus or not and it's throwing an error.
<View style ={
{...styles.inputRow, focus? ...bgColorOnFocus: ...bgColorOnBlur}}
>
I'm not sure if I'm being silly, or if there's something missing that I'm not familiar with? Thanks for looking into this!

Typically a style prop accept an array as a value:
<View style={[styles.inputRow, isFocus ? bgColorOnFocus : bgColorOnBlur]}>
is a valid way to conditionally render some style.

Related

React adding conditional styling (&&)

the same newbie here.
I have the code which working as intended, all I want to do is add && with state variable (boolean) to make a text have line-through decoration while clicked (using crossedItemOnClick)
import React, { useState } from 'react';
import ReactDOM from 'react-dom';
function Para(props) {
const {id, className, info, state} = props;
return (
<p onClick={props.crossedItemOnClick} id={id} className={className} style={{textDecorationLine: 'line-through'}}>{info}</p>
)
}
export default Para;
My whole page disappears if I change it to:
<p onClick={props.crossedItemOnClick} id={id} className={className} style={state && {textDecorationLine: 'line-through'}}>{info}</p>
I'd love to know what I'm doing wrong and why the page completely disappears. And of course explanation to learn if you'd be kind to.
Much thanks!
style attribute takes object as value in react. Correct way will be:
<p onClick={props.crossedItemOnClick} id={id} className={className} style={state ? {textDecorationLine: 'line-through'}:{}}>{info}</p>
assuming you have to apply the style when state is true.
That is because style prop expect object of type CSSProperties, and in case when your condition is false you will end up with something like style={false} which will cause your app to crash since you provided HTML element with invalid styling.
Easiest solution is to just rewrite that part to style={state ? {...someStyle} : {}}
Your approach is mostly correct, however, your condition should be applied on the style property directly like this if you want to use &&
style={{textDecorationLine: state && 'line-through'}}

Add attribuites to JSX.Element after declaration

If I have an element in let's say a dictionary like this:
let item = {
element: <myElement/>,
color: "#0e76a8"
}
Can I in render() add style attribute to the item.element ?
I imagined something like this would work just fine but it doesn't
return (
<div>
{<item.element style={{ color: "item.color" }/>}
</div>
);
Is there a way in JSX or React to achieve what I want?
Note: My goal isn't being able to change item.color. It's just I need abstraction because in my code I'll have list of different item variable and render each one with the desired attributes, I don't want to hardcode it in JSX to be easier to find in case of any future changes.
As you already know, placing < and > around a function reference in JSX will apply that functional component.
You don't want to apply your component when defining your item object, you only want to keep a reference to it for application later:
let item = {
element: myElement,
color: "#0e76a8"
}
Also, as noted in comments, you're setting the color CSS style to the string "item.color", whereas we can safely assume you mean color: item.color
I think you wanna do that:
return (
<div>
{<item.element style={{ color: item.color }/>}
</div>
);

Conditionally activate Material UI tooltip?

I have the following React component using Material UI:
const MyButton = ({ warningText }) => (
<Tooltip title={warningText}>
<Button>Do action</Button>
</Tooltip>
)
Currently, this shows an empty tooltip when warningText is undefined. Instead I would like to show no tooltip at all. Is there a way to conditionally surpress the tooltip in these cases?
Off course I could just use an if statement to not render the tooltip component, but this would lead to rather ugly code in my opinion.
Should be
<Tooltip title={warningText == null ? "" : warningText}>
<Button>Do action</Button>
</Tooltip>
the docs say that it won't be displayed if the string length is zero.
https://material-ui.com/api/tooltip/
Tooltip title. Zero-length titles string are never displayed.
If you're looking to manually play around for customization, you can try to use the following solution:
As per the documentation, you can use the open prop and mouse events to handle it manually.
In the following scenario, we will use state to set showing the tooltip when we enter the mouse over the element, and we will also use text && to assert that text has a value, this will prevent the tooltip from showing when text is undefined.
const [showTooltip, setShowTooltip] = useState(false);
<Tooltip
open={text && showTooltip}
onMouseEnter={() => { setShowTooltip(true) }}
onMouseLeave={() => { setShowTooltip(false) }}
placement="top" title={text}
>
<div>
{text}
</div>
</Tooltip>
Note, the mui-tooltip is not a perfect component to begin with and is not very straight forward, this solution works for me but might not work in your situation as is, I will try to put it out, you can try to make it work on your end.
If it doesn't work for you, please leave a comment and I'll try to help.
You should take a look at https://material-ui.com/api/tooltip/
There are options like
disableFocusListener
disableHoverListener
disableTouchListener
interactive
I think interactive={true} should fit your needs best
<Tooltip title={warningText} interactive={!warningText}>...</Tooltip>

How to add style - like margin - to react component?

So, expect two simple components that I have built:
import {Input} from 'semantic-ui-react';
import {Select} from 'semantic-ui-react';
const CategoriesDropdown = ({categories, onCategorySelected, selectedCategory}) => {
const handleChange = (e, {value})=>{
onCategorySelected(value);
};
return (
<Select placeholder="Select category" search options={categories} onChange={handleChange} value={selectedCategory} />
);
};
const IdentifiersInput = ({identifiers, onIdentifiersChanged}) => {
return (
<Input placeholder="Enter identifiers..." value={identifiers} onChange={onIdentifiersChanged}/>
);
};
Nothing fancy so far.
But now, I am building another component that displays those two in a flexbox row:
<Box>
<CategoriesDropdown categories={categories} selectedCategory={selectedCategoryId}
onCategorySelected={this.selectCategory}/>
<IdentifiersInput identifiers={identifiers} onIdentifiersChanged={this.changeIdentifiers}/>
</Box>
Unfortunately they are both displayed right next to each other without any margin in between.
Usually, I would just add a margin-left style to the second element, but because it is a React component, that doesn't work. Using style={{marginLeft: '20px'}} doesn't work as well, because the IdentifiersInput component doesn't use it.
I know that I can fix it by doing this: <Input style={style} ... inside the IdentifiersInput component.
However, this seems to be a very tedious way of achieving this goal. Basically, I have to add this to every single component I am writing.
I clearly must be missing something here. How am I supposed to apply such layout CSS properties to React components?
I think I understand.
1) Applying CSS directly to React Components does not work--I can confirm that.
2) Passing props down to the low level elements is tedious, confirmed but viable.
Notice hasMargin prop:
<Box>
<CategoriesDropdown
categories={categories}
selectedCategory={selectedCategoryId}
onCategorySelected={this.selectCategory}
/>
<IdentifiersInput
identifiers={identifiers}
onIdentifiersChanged={this.changeIdentifiers}
hasMargin
/>
</Box>
Possible input:
const IdentifiersInput = ({identifiers, onIdentifiersChanged, className, hasMargin }) => {
return (
<Input
className={className}
placeholder="Enter identifiers..."
value={identifiers}
onChange={onIdentifiersChanged}
style={hasMargin ? ({ marginLeft: '0.8rem' }) : ({})}
/>
);
};
NOTE: I do not like style as much as I like adding an additional class because classes can be adjusted via media queries:
const IdentifiersInput = ({identifiers, onIdentifiersChanged, className, hasMargin }) => {
const inputPosition = hasMargin ? `${className} margin-sm` : className
return (
<Input
className={inputPosition}
placeholder="Enter identifiers..."
value={identifiers}
onChange={onIdentifiersChanged}
/>
);
};
If you find inputPosition too verbose as shown above:
className={hasMargin ? `${className} margin-sm` : className}
3) You could accomplish it using a divider Component, sacreligious yet rapidly effective
<Box>
<CategoriesDropdown
categories={categories}
selectedCategory={selectedCategoryId}
onCategorySelected={this.selectCategory}
/>
<div className="divider" />
<IdentifiersInput
identifiers={identifiers}
onIdentifiersChanged={this.changeIdentifiers}
/>
</Box>
You can use media queries and control padding at any breakpoints if desired.
4) CSS pseudo-elements or pseudo-classes, I don't see any mention of them in answers so far.
MDN: https://developer.mozilla.org/en-US/docs/Web/CSS/Pseudo-classes
CSS Tricks: https://css-tricks.com/pseudo-class-selectors/
Usually, when you have a random collection of DOM elements, you can calculate a way using CSS to wrangle them into the correct position. The list of available pseudo-classes is in that MDN link. It honestly helps to just look at them and reason about potential combinations.
My current issue is I don't know what is in <Box /> other than it probably has a div with display: flex; on it. If all we have to go on is that and the div is called <div className="Box">, maybe some CSS like this will fix it:
.Box {
display: flex;
}
.Box:first-child {
margin-right: 0.8rem;
}
This is why it is extremely important to know exactly what the surrounding elements will or can be, and exactly which CSS classes/IDs are nearby. We are basically trying to hook into something and correctly identify the left child in Box and add margin to the right of it, or target the right child and add margin to the left of it (or depending on everything, target both and split the additional margin onto both).
Remember there is also ::before and ::after. You are welcome to get creative and find a solution that involves position:relative and position: absolute and adds no markup.
I will leave my answer like that for now, because I think either you already thought about pseudo-selectors, or you will quickly find something that works :)
That or the divider is actually quite viable. The fact you can use media queries alleviates you from concern of future management or scalability of the components. I would not say the same about <div style={{}} />.
As your component specializes another single component it would be a good practice to pass any props your wrapper does not care for to the wrapped component. Otherwise you will loose the ability to use the api of the original <Input>component including passing styles to it:
const IdentifiersInput = ({identifiers, onIdentifiersChanged, ...props}) = (
<Input
{...props}
placeholder="Enter identifiers..."
value={identifiers}
onChange={onIdentifiersChanged}
/>
);
There may be valid cases where you explicitly want to prevent users to be able to pass props to the wrapped component but that does not look like one of those to me.
I clearly must be missing something here. How am I supposed to apply
such layout CSS properties to React components?
You did not miss something. A react component has no generic way to be styled because it is no DOM element. It can have a very complicated and nested DOM representation or no representation at all. So at some point you as the designer of the component have to decided where the styles, ids and class names should be applied. In your case it is as easy as passing these props down and let the <Input> and <Select>component decide. I find that to be quite elegant rather than tedious.
I see several ways to do it, but the easiest I see would be to pass a className to IdentifiersInput like so:
<IdentifiersInput className="marginLeft" identifiers={identifiers} onIdentifiersChanged={this.changeIdentifiers}/>
Inside IdentifiersInput I would just set that class to the Input:
const IdentifiersInput = ({identifiers, onIdentifiersChanged, className}) => {
return (
<Input className={className} placeholder="Enter identifiers..." value={identifiers} onChange={onIdentifiersChanged}/>
);
};
Semantic UI's Input element can receive a className prop.
I would then just use CSS or SCSS to add styles to that particular class. In this case, the margin you want.

React Native: How do I style NavigationExperimental's NavigationCardStack and NavigationHeader?

I am trying to change the background color of NavigationExperimental's NavigationCardStack by: style={{backgroundColor:'white'}} but it does not work.
Is there a way around it? Or is it not possible?
Also, how can I hide the NavigationHeader's 1px hairline? I tried doing shadowHidden={true} but it is not provided unfortunately.
Your style prop is correct. But where are you applying it? You should apply this prop to NavigationHeader.
<NavigationHeader
style={{backgroundColor:'blue'}}
...
You are probably rendering the NavigationHeader in a separate function, maybe named renderHeader(). This function is passed as a prop to NavigationCardStack
<NavigationCardStack
onNavigateBack={this.props.onNavigateBack}
renderHeader={this.renderHeader}
...
edit: answer to the question about the hairline:
style={{backgroundColor:'blue', borderBottomWidth: 0}}
Here is the RN code for the hairline style, as you can see it's platform dependent:
borderBottomWidth: Platform.OS === 'ios' ? StyleSheet.hairlineWidth : 0,

Categories

Resources