I have a simple React component that accepts a prop that hides/shows a component's children. I hide/show using the style attribute and display: none. If I use display: none !important, the component no longer re-renders when it receives new props.
Within the render method, it looks like this:
var style = {};
if(shouldHide) {
//this works
style = {
display: 'none'
};
//this does not
//style = {
// display: 'none !important'
//};
}
return (
<span style={style} {...this.props} />
);
Here's the full example: https://jsfiddle.net/ccnokes/LnrnrLy2/ (relevant lines start at line 27)
What's going on here? Am I missing something?
!important is currently unsupported - https://github.com/facebook/react/issues/1881
Does not appear they will be adding it any time soon.
They offer this as a workaround:
var sheet = document.createElement('style');
document.body.appendChild(sheet);
sheet.innerHTML = 'html {font-size: 1em !important;}';
But not sure if you want to go down that path or not.
I was able to resolve with a class switch:
//css
.hidden {display: none !important};
//jsx
var hideClass;
if(shouldHide) {
hideClass = "hidden";
}
return (
<span className={hideClass} {...this.props} />
);
Updated
I went ahead and added the workaround above.
And the fiddle here - https://jsfiddle.net/kellyjandrews/oan4grme/
Not exactly the answer you wanted, but it works :)
Related
I appreciate this may be a little basic here, but I'm relatively new to React and am testing the waters with various ways of applying on-the-fly styling rather than creating separate stylesheets and importing them.
I'm trying to experiment adding styles to three different elements - one via inline styles, another via a style tag, and another via a style variable - where only the inline style seems to work.
Here is my code with all 3 elements:
import React from 'react'
const App = () => {
render {
const testOneStyle = {
color: "red",
fontWeight: "bold"
};
return (
<div>
<span style={testOneStyle} className="test-one">test 1</span>
<span className="test-two">test 2</span>
<style>
.test-two {
color: red;
font-weight: bold
}
</style>
<span style={{color: "red"}} className="test-three">test 3</span>
</div>
)
}
}
export default App
Firstly, does the variable style (i.e. here) only work with class components rather than functional components?
And can someone explain why this is not rendering and how to render and apply the styles?
Thank you for any advice. Here is a StackBlitz demo: https://stackblitz.com/edit/react-tjukup
Unfortunately, <style> tags don't work in JSX the way they do in html. You are going to have to parse the the string appropriately yourself, since JSX is just javascript with syntactical sugar to convert into React.createElement() function with the right parameters. So you want to generally avoid style and head tags in JSX, but if you do, you want to use it like:
<style>
{"\
.test-two {\
color: red;\
font-weight: bold;\
}\
"}
</style>
EDIT
Also, to answer your question "does the variable style only work with class components rather than functional components?", no. The prop style is a JSX prop and works regardless of what kind of component you are using.
EDIT
And the reason why your component is not rendering, is because render() is a function that is only used in class based components. In a functional component you just directly return the JSX.
import React from "react";
const App = () => {
const testOneStyle = {
color: "red",
fontWeight: "bold"
};
return (
<div>
<span style={testOneStyle} className="test-one">
test 1 - fails
</span>
<span className="test-two">test 2 - fails</span>
<style>
{`
.test-two {
color: red;
font-weight: bold
}
`}
</style>
<span style={{ color: "red" }} className="test-three">
test 3 - works
</span>
</div>
);
};
export default App;
EDIT
As you may have observed in the snippet I have provided, you can also use strings with "`" to make it easier to enter strings in JSX
I hope this may helps you
import React from 'react'
const App = () => {
const testOneStyle = {
color: "blue",
fontWeight: "bold"
};
return (
<div>
<span style={testOneStyle} className="test-one">
test 1 - fails
</span>
<span className="test-two">
test 2 - fails
</span>
<style>
{
`.test-two {
color: green;
font-weight: bold
}`
}
</style>
<span style={{color: "red"}} className="test-three">
test 3 - works
</span>
</div>
)
}
export default App
Explaination:
You are using functional component
Class component require render method to return a JSX. Functional component can directly return JSX.
you can add style tag in your JSX but the context inside need to be string.
Basic scenario is such: I have a component which has width: 100% as defined in a stylesheet. Therefore it should retain the width of its parent component. I want to calculate the width of my component and apply it to my child component because I am rendering it via createPortal and I would like them to be the same width. This works in the browser. However, in my test, I am finding that window.getComputedStyle(component) is not returning any of the styles applied from the stylesheet.
As suggested, I could mock the javascript window, but that's actually counter to what I'm hoping to do, I think. I want to verify the behavior that is present in the browser, that window.getComputedStyle() returns all styles applied, not just the inline styles.
I have put a simple example into a codesandbox: https://codesandbox.io/s/goofy-wilson-6v4dp
Also here:
function App() {
return (
<div className="App">
<WidthComponent />
</div>
)
}
function WidthComponent() {
const myInput = useRef();
const [inputWidth, setInputWidth] = useState(0);
useEffect(() => {
console.log("in handleLoad");
const width = myInput.current ? myInput.current.offsetWidth : 0;
setInputWidth(width);
}, [myInput]);
return (
<div className="inherited-width" ref={myInput}>
<div style={{ width: inputWidth }} className="child-element">
Hello
</div>
</div>
);
}
// test
test("width is inherited", () => {
const { rerender } = render(
<div style={{ width: "452px" }}>
<WidthComponent />
</div>
);
const element = document.getElementsByClassName("child-element").item(0);
rerender(
<div style={{ width: "452px" }}>
<WidthComponent />
</div>
);
expect(window.getComputedStyle(element).width).toBe("452px");
});
.App {
font-family: sans-serif;
text-align: center;
width: 500px;
}
.inherited-width {
width: inherit;
}
Any help is appreciated.
However, in my test, I am finding that window.getComputedStyle(component) is not returning any of the styles applied from the stylesheet.
Note that if you're running your tests in JSDOM (i.e. every Jest test) then CSS isn't fully implemented. Specifically, the cascade part of CSS is not implemented (https://github.com/jsdom/jsdom/pull/2690). Inheritance is only partially implemented (display and visibility) (https://github.com/jsdom/jsdom/issues/2160).
I would suggest running tests that assert on computed styles only in browsers, not JSDOM. A codesandbox test is not running in an actual browser environment.
I have a simple message board with nested comments built in React. I'm trying to add a favorite button (just a button that toggles filled/unfilled contingent on its boolean value). Every tutorial I find involves making a new component, but I'd like to include this in my primary app code (using a hook/useState).
I've tried some various CSS stuff like active, etc. I'm rusty with CSS and a bit lost using it on JSX. Ideally, it'd be a star button that fills/unfills, but I want to figure out the simple basics here first.
function Toggle(props) {
const [toggleState, setToggleState] = useState(false);
function toggle() {
setToggleState(toggleState === false? true : false);
}
return (
<div {...props}>
<Button
className={`switch ${toggleState}`}
onClick={toggle}>
Favorite
</Button>
</div>
)
}
Toggle=styled(Toggle)`
//dunno what to put here
The button shows up just fine but nothing I try in the style will make it toggle colors (or filled/unfilled) on click. How can I do this?
First of all this syntax is more readable
function toggle() {
setToggleState(!toggleState);
}
then you can use this to change the class of your button
className={`switch ${toggleState ? "some_class" : "some_other_class"}`}
If you use styled-components, you can customize Button directly. In Tagged templates, switch styles according to prop.
Demo
const Button = styled(Icon)`
color: ${props => (props.filled ? 'pink' : 'transparent')};
-webkit-text-stroke: 2px pink;
overflow: hidden;
margin: 0 0.5em;
cursor: pointer;
`
function Toggle(props) {
const [toggleState, setToggleState] = useState(false)
function toggle() {
setToggleState(toggleState => !toggleState)
}
return (
<Button filled={toggleState} onClick={toggle}>
favorite
</Button>
)
}
Adapting based on props
Following your code:
const StyledToggle=styled(Toggle)`
&.true {
//true styles
}
&.false {
//false styles
}
`;
As you are using css in js you could also pass the state as a prop to the component and read it inside the tagged template:
return (
<div {...props}>
<Button
toggleState={toggleState}
onClick={toggle}>
Favorite
</Button>
</div>
)
}
const StyledToggle = styled(Toggle)`
font-weight: ${({toggleState}) => toggleState && "bold"};
`;
In App.css, I have
.theme {
color: green;
}
And I have className="theme" scattered in multiple components.
Is there a way to change the theme color from green to blue on an event?
If not, how should I design my code?
Well, You can create 2 classes named .blue-theme and .green-theme
Whenever, some event occurs,
onClick = (themeCol) => {
this.setState({theme:thmeCol})
}
render(){
return(
<button onClick={()=>onClick('blue-theme')}>Blue theme</button>
<button onClick={()=>onClick('green-theme')}>Green theme</button>
<div className={this.state.theme}> Sample </div>
)
}
You can pass the value of theme.
you can try
const themeClass = "";
if (event-as-wanted){
themeClass="theme";
}
className={themeClass}
also you can use style insted of className in same file
const theme ={
color: '';
};
style={theme}
and change it with events like
if (event-as-wanted){
theme.color = "green";
}
You can conditionally render the <style/> tag to override style definition for the class in the whole document.
class App extends React.Component {
state = {
red: true,
}
render() {
return (
<div>
<div className="foo">Foo</div>
<button onClick={() => this.setState({ red: !this.state.red })}>
Toggle Red
</button>
{this.state.red && <style>{".foo { color: red }"}</style>}
</div>
);
}
}
Keep in mind that inside JSX tags, curly brackets will be picked up by the interpreter and may break the parser. To avoid that, you should put your CSS inside a string like in the example above.
Adding a <style/> tag to CSS document will override any equally specific CSS rules that came before that. Once the condition is no longer met, the style tag will be removed and the original styling will be restored.
in react.js just set the state of color to whatever and on a click event toggle the color
class App extends React.Component {
constructor(props) {
super(props);
this.state = {color: green};
this.changeColor = this.changeColor.bind(this);
}
changeColor() {
const newcolor = this.state.color == green ? blue : green;
this.setState({ color: newcolor});
}
render() {
return (
<div class="theme" style={{color: this.statecolor}}>
<button onClick={this.changeColor}>
</button>
//put all html withing the parent DOM of element with class theme accordingly or it wont render.
</div>
);
}
Make two class, .green-theme{
color:'green'} and similarly, blue theme.
Mantain a REDUX STATE, CURRENT_THEME. Upon event fire, change the redux state accordingly and everywhere, where you want to use CURRENT_THEME, use it using mapStateToProps.
I would rather try to use almost pure CSS solution:
in App.css
#App.first-theme .theme { color:green; }
#App.second-theme .theme { color:blue; }
in App's render:
<div id="App" className={this.state.currentTheme}>
<AnotherComponent1 />
<AnotherComponent2 />
</div>
All you need to do is to change this.state.currentTheme appropriately. You can even use prop injected from the redux.
Almost all other solutions posted here have the same flaw: you have to adapt all your components to use the theme. Using this solution, you are able to change app's appearance without additional code in your components.
Trust me, injecting the same property from redux store/react context for every component will give you headaches and a lot of unnecessary code.
You should also try to avoid generating additional <style> tags - you will end up having plenty of !important and HTML, logic, and CSS in one file. What a mess! Imagine, what would happen if you would like to use SCSS in the future...
When using ordinary CSS if you want to style your place holder you use these css selectors :
::-webkit-input-placeholder {
color: red;
}
But I can't figure out how to apply these type of styles in react inline styles.
Simply give your "input" tag an "id" or a "class" and put your css style in App.css inside src. e.g
//App.css or external stylesheet
#inputID::placeholder {
color: #ff0000;
opacity: 1;
}
//your jsx code
<input type="text" id="inputID" placeholder="Your text here" />
That actually worked for me.
You can't use ::-webkit-inline-placeholder inline.
It is a pseudo-element that (much like e.g. :hover) can only be used in your stylesheet:
The non-standard proprietary ::-webkit-input-placeholder pseudo-element represents the placeholder text of a form element.
Source
Instead, assign a class to the React component via the className property and apply the style to this class.
You could try to use radium
var Radium = require('radium');
var React = require('react');
var color = require('color');
#Radium
class Button extends React.Component {
static propTypes = {
kind: React.PropTypes.oneOf(['primary', 'warning']).isRequired
};
render() {
// Radium extends the style attribute to accept an array. It will merge
// the styles in order. We use this feature here to apply the primary
// or warning styles depending on the value of the `kind` prop. Since its
// all just JavaScript, you can use whatever logic you want to decide which
// styles are applied (props, state, context, etc).
return (
<button
style={[
styles.base,
styles[this.props.kind]
]}>
{this.props.children}
</button>
);
}
}
// You can create your style objects dynamically or share them for
// every instance of the component.
var styles = {
base: {
color: '#fff',
// Adding interactive state couldn't be easier! Add a special key to your
// style object (:hover, :focus, :active, or #media) with the additional rules.
':hover': {
background: color('#0074d9').lighten(0.2).hexString()
},
'::-webkit-input-placeholder' {
color: red;
}
},
primary: {
background: '#0074D9'
},
warning: {
background: '#FF4136'
}
};
Do not use ::-webkit-inline-placeholder inline and Radium for one placeholder.
I suggest go to your index.css
input.yourclassname::placeholder { /* Chrome, Firefox, Opera, Safari 10.1+ */
color: white;
opacity: 1; /* Firefox */
}
My approach is to simply apply different styles to the entire <input /> component based on whether or not the value is empty. No need to install a new dependency, and no need to use a stylesheet which seems to be the point of the original question.
var inputStyles = {
border: '1px solid #cbcbcb',
color: '#525252',
};
var placeholderStyles = {
...inputStyles,
color: '#999999',
};
<input
type="text"
placeholder="enter text here"
value={myValue}
style={myValue ? inputStyles : placeholderStyles}
/>
For me, I use Radium's Style component. Here's what you can do in ES6 syntax:
import React, { Component } from 'react'
import Radium, { Style } from 'radium'
class Form extends Component {
render() {
return (<div>
<Style scopeSelector='.myClass' rules={{
'::-webkit-input-placeholder': {
color: '#929498'
}}} />
<input className='myClass' type='text' placeholder='type here' />
</div>
}
}
export default Radium(Form)
Use Template Literals i.e. Backward quotes(``) for adding your pseudo-element.
const inputFieldStyle = `
.inputField::-webkit-input-placeholder{
color: red;
}`
Using class is important thing so make sure you used class before pseudo-element.
Then you can use tag where you pass above style like below:
const reactFunctionalComponent = (props) => {
...
return(
<>
<style>
{inputFieldStyle}
</style>
...
</>
)
}
Here is a method that provides pseudo selector functionality in React, without relying on a third party library, in about 20 lines of code.
This is an extension of RohitSawai's answer that injects a <style> tag, but in a more encapsulated and reusable fashion.
Define this function once in your project:
/** Returns a unique className and injectStyle function that can be used to
style pseudo elements. Apply the className to the desired element and render
injectStyle() nearby. The pseudo selector, e.g. ::-webkit-input-placeholder,
will be appended to the unique className, styling a pseudo element directly,
or a descendant pseudo element, as determined by the selector. */
const pseudo = (pseudoSelector, style) => {
const className = `pseudo-${Math.floor(Math.random() * 1000000)}`
// simple encoding of style dictionary to CSS
// for simplicity, assumes that keys are already in slug case and units have
// been added, unlike React.CSSProperties
const styleCSS =
'{' +
Object.entries(style)
.map(([name, value]) => `${name}: ${value};`)
.join('') +
'}'
return {
className,
injectStyle: () => (
<style>
{`.${className}${pseudoSelector} ${styleCSS}`}
</style>
)
}
}
And use it like this:
const MyComponent = () => {
const placeholder = pseudo('::-webkit-input-placeholder', { color: 'red' })
return (<div>
{placeholder.injectStyle()}
<input className={placeholder.className} placeholder="This placeholder is red" />
</div>)
}