So essentially I have a React component <Bore />. And I have an array of Bores and I need to style the first and last element of the array. I know how to access these elements with Bores[0] and Bores[Bores.length-1]. But my problem is figuring out how to style these specific components after creation. Would I have to do something like className += "newClass". I'm only 2 days into using React so any help would be greatly appreciated.
You can use style objects instead of mutating the class list. The important thing to remember is that CSS properties are camel case. Something like
class Parent extends Component {
constructor(props){
super(props);
this.state = {
style: {
backgroundColor: "green",
marginRight: "10px"
}
}
}
changeStyle = () => {
this.setState(() => {
return {
style: {
marginLeft: "10px",
backgroundColor: "red"
}
}
})
}
render = () => {
return (
<div>
<Child style={this.state.style} changeStyle={this.changeStyle}
</div>
)
}
}
const Child = ({ style, changeStyle }) => {
return (
<div style={style} onClick={changeStyle}>
<h1>Dummy</h1>
</div>
)
}
https://jsfiddle.net/rfhmxts2/ see here, click on the div to change it's background color and margins
Related
I'm very new to react. And I'm trying to learn some new stuff. So what I want to do is to add CSS within my Header.js file, And I don't know how to do that. Because I don't want to import external or inline CSS. But rather use it like in Html with tag on the header. But not just that, I want to use that CSS specifically for the file, in this case, Header.js.
This might help
Header.js
const styles = StyleSheet.create({
container: {
flex: 1,
alignItems: 'center',
justifyContent: 'center',
// alignSelf: 'center',
},
textStyle: {
marginTop: Metrics.ratio(0),
marginHorizontal: Metrics.ratio(70),
fontFamily: Fonts.type.base,
fontSize: Fonts.size.normal,
color: Colors.black,
},
});
class Header extends React.Component {
render() {
return (
<div style={ styles.color } />
);
}
}
You have several possibilites.
The simplest is to use css code directly in the element like
<div style={{color:'#000',backgroundColor:'#fff'}}>
...
</div>
Or you can use the libraries for that, like styled-components (https://styled-components.com/) for this.
You need to import this:
import styled from 'styled-components';
Then you can define your element css on the top of the page, i.e. SomeCSSStyling
const SomeCSSStyling = styled.div`
color:#000;
background-color:#fff;
`
Then you can use this constant in the class code of the react component:
class MyReactComponent extends Component {
constructor(props) {
...
}
render() {
return (
<SomeCSSStyling>
...
</SomeCSSStyling>
);
}
}
export default MyReactComponent;
UPDATE:
You can also define :hover or ::before etc. with style-components:
const SomeCSSStyling = styled.div`
color:#000;
background-color:#fff;
&:hover{
font-weight:bold;
}
`
You can create style object inside react component like this:
const myDivStyles = {
color: "red",
fontSize: "32px"
}
All propertis are the same like in CSS but this one with "-" sign change in to camelCase nams, e.g. font-size change to fontSize, background-color change to backgroundColor.
Then you can add this style to elements in your components by style attribute.
render() {
return (
<div style={ myDivStles } />
)
}
You can also describe style without creating style object like this:
<div style={{ color: "red", backgroundColor: "#fff" }} />
Be sure you are using double closure {{ }}.
EDIT
With :hover selector
You have two prosibilites. First you can use component state to determinate if component is hovered and then prepare correct style, e.g:
class Component extends React.Component {
constructor(props) {
super(props)
this.state = {
hovered: false
}
this.toggleHover = this.toggleHover.bind(this)
}
toggleHover(state) {
this.setState({
hovered: state
})
}
render() {
const styles = {
color: this.state.hovered ? "red" : "blue"
}
return (
<div style={styles} onMouseEnter={ () => this.toggleHover( true ) } onMouseLeave={ () => this.toggleHover( false ) }>
Text
</div>
);
}
}
Second you can use js styled components syntax and refer to other component, you can read more about this here: https://styled-components.com/docs/advanced#referring-to-other-components
But to be honest when I dealing with :hover or other selectors I prefer using default css files or much more often scss files prepared for components. So when I have e.g Button component in same location I have Button.css ( or Button.scss ) file where I can work with standard css. After this I have css files connected with components which should handled them.
I'm using React to create a component that will render a different background depending on its property. However, I don't know how to change its property without collapsing the React frame.
Here's my code:
const inactiveStyle = {
backgroundColor: "",
color: "white",
};
const activeStyle = {
color: "black"
};
export default class DrumPad extends Component {
constructor(props) {
super(props);
this.state = {
style: inactiveStyle,
};
render(){
inactiveStyle.backgroundColor = this.props.backgroundColor
return (<div style={this.state.style}></div>)
When I tried it, this is the error I get:
TypeError: Cannot assign to read only property 'backgroundColor' of object '#'
I have tried following the advice here but it doesn't work, because my original code is already a constant, not a state itself. Please help.
I don't feel like you need state at all here since it's based on the props value or a default.
const DrumPad = ({ backgroundColor, ...props }) => (
<div
style={{
backgroundColor,
color: 'white',
}}
>
...
</div>
);
You've not shared all the relevant code, but if you want to toggle on some isActive prop, you can still do it like this:
const inactiveStyle = { color: 'white' };
const activeStyle = { color: 'black' };
const DrumPad = ({ isActive, backgroundColor, ...props }) => (
<div
style={{
...(isActive ? activeStyle : inactiveStyle),
backgroundColor
}}
>
...
</div>
);
If, for some reason, you'd still like to use the component's state, make a copy of the default style object and use the props to initialize the state, then use this.setState to update it.
Note that there's no benefit at all to the following code and I've just put it together as an example of how the setState API works.
const inactiveStyle = { color: 'white' };
const activeStyle = { color: 'black' };
class DrumPad extends Component {
state = {
isActive: false,
style: {
...inactiveStyle,
backgroundColor: this.props.backgroundColor
}
}
onClick = () => {
// toggle the active state and style.
this.setState(({ style, isActive }) => ({
isActive: !isActive,
style: {
...style,
...(!isActive ? activeStyle : inactiveStyle),
},
}));
}
render() {
return (
<div style={this.state.style} onClick={this.onClick}>
...
</div>
);
}
}
If you'd like the state to be updated based on both props and the previous state when any of them changes, there's now getDerivedStateFromProps.
This might refer to other relevant general questions like how to update a child component from the parent, though I'd like to hear any fair judgement of my design solution to the following scenario.
I have a parent class where I store css attributes for 2 children objects.
import React from 'react'
import Item from './item/Item'
class Small_gallery extends React.Component {
constructor(props) {
super(props);
this.state = {
chosenVal: 0,
};
this.listObjParams = [
// Style 1
{
left: 300,
zIndex: 0
},
//Style 2
{
left: 320,
zIndex: 1
}
];
this.handleClick = this.handleClick.bind(this);
this.calculateShift = this.applyNewStyle.bind(this);
this.listItems = this.listObjParams.map((objStyle, i) =>
<Item
key={i}
id={i}
objStyle={objStyle}
onClick={this.handleClick}
/>
);
}
handleClick = (indexFromChild) => {
this.setState({chosenVal: indexFromChild});
this.applyNewStyle(indexFromChild)
};
applyNewStyle = (clickedIndex) => {
if (clickedIndex === 0) {
// somehow I want to apply new css style 2 to the clicked? <Item> child
};
render() {
return (
<div>
{this.listItems}
</div>
)
}
Child component is rather trivial:
class Item extends React.Component {
constructor(props) {
super(props)
}
render() {
return (
<div
onClick={(e) => {
e.preventDefault();
this.props.onClick(this.props.id)
}}
style={{
left: this.props.objStyle.left,
zIndex: this.props.objStyle.zIndex
}}
>
</div>
);
}
}
The question is: how can I apply style 1 or 2 to the clicked Item component(depending on the index I am returning)? I've read about getDerivedStateFromProps instead of using deprecated componentWillReceiveProps here https://hackernoon.com/replacing-componentwillreceiveprops-with-getderivedstatefromprops-c3956f7ce607 but it's not a solution for me.
I expect number of created Items to grow in the future to 10-20, so it makes no sense to populate state of Item with this.listObjParams when creating it, or am I wrong here?
I have a working example below, so to cover what I did:
Create a prop that takes an array of items, more items more looped <Item />'s will appear.
Styles are either activeStyles || inactiveStyles it is based on the currentId matching the id from object (from array prop = items).
import React from "react";
const inactiveStyles = {
left: 300,
zIndex: 0,
backgroundColor: "#E9573F"
};
const activeStyles = {
left: 320,
zIndex: 1,
backgroundColor: "#00B1E1"
};
const inboundItems = [
{
id: 0
},
{
id: 1
},
{
id: 2
}
];
// Note - added to show it working not needed
const defaultStyles = {
display: "block",
border: "1px solid black",
width: 50,
height: 50
};
export const Item = ({ id, onClick, style }) => (
<>
<pre>{JSON.stringify({ styles: style }, null, 2)}</pre>
<div
{...{ id }}
style={{ ...defaultStyles, ...style }}
onClick={e => {
e.preventDefault();
onClick(id);
}}
/>
</>
);
export const SmallGallery = ({ items = inboundItems }) => {
const [currentId, setCurrentId] = React.useState(null);
const getStyles = selectedId => {
return currentId === selectedId ? activeStyles : inactiveStyles;
};
return items.map(({ id, ...item }) => (
<Item
key={id}
{...{ id }}
{...item}
style={getStyles(id)}
onClick={selectedId => setCurrentId(selectedId)}
/>
));
};
export default SmallGallery;
Let me know what you think, I added a screenshot to show styles being added.
For <Item/> you can use simple functional component. Optimal for simple, not so complex use cases.
E.g
const Item = ({ id, clickHandler, objStyle }) => (
<div
onClick={e => {
e.preventDefault();
clickHandler(id);
}}
style={...objStyle}
/>
);
PureComponent will be updated on props change, too.
In full class component you can use shouldComponentUpdate() to force rerendering on props change. No need to duplicate data (into state) using getDerivedStateFromProps (depends on use case).
Search for some tutorials (f.e. typical todo examples) since you have no idea about state management, updating etc.
Placing listObjParams outside of state won't force rerendering on update. BTW it looks more like a style pool - maybe you should have a child params array... you can combine it with style index array or keep them (and pass as props) separately.
constructor(props) {
super(props);
this.state = {
// chosenVal: 0, // temporary handler param? probably no need to store in the state
listObjStyles: [0, 1] // style indexes
};
this.stylePool = [
// Style 1
{
left: 300,
zIndex: 0
},
//Style 2
{
left: 320,
zIndex: 1
}
];
usage:
this.listItems = this.state.listObjStyles.map((styleIndex, i) => <Item
key={i}
id={i}
objStyle={this.stylePool[ styleIndex ]}
clickHandler={this.handleClick}
/>
Updating listObjStyles (setState()) will force rerendering, updating this.stylePool won't (move to the state if rerendering required).
Of course stylePool can contain more than 2 styles for different item 'states'. You can make styles for selected, liked, unliked - by storing indexes in an array you can mix any of them with custom logic (f.e. only one selected, many liked).
10-20 items is not the case where you need special optimizations (other than avoiding unnecessary rerenderings).
Just to sum up what I've done to make it all work based on two answers (still a rather toy example):
Parent:
import Item from './item/Item'
class Small_gallery extends React.Component {
constructor(props) {
super(props);
this.state = {
listObjStyles: [0, 1]
};
this.stylePool = [
{
position: 'absolute',
width: 600,
left: 300,
height: 100,
backgroundColor: '#000',
zIndex: 0,
transition: 'all 1s ease'
},
{
position: 'absolute',
width: 600,
left: 720,
height: 350,
backgroundColor: '#ccc',
zIndex: 1,
transition: 'all 2s ease'
}]
}
handleClick = (indexFromChild) => {
console.log(indexFromChild)
if (indexFromChild === 0) {
this.setState({
listObjStyles: [1, 0]
})
} else if (indexFromChild === 1) {
this.setState({
listObjStyles: [0, 1]
})
}
}
render() {
return (
<>
<div style={{display: 'flex', margin: 40}}>
{this.state.listObjStyles.map((styleIndex, i) =>
<Item
key={i}
id={i}
objStyle={this.stylePool[styleIndex]}
onClick={this.handleClick}
/>
)}
</div>
</>)
}
}
Child:
const Item = ({id, onClick, objStyle}) => (
<div
onClick={e => {
e.preventDefault();
onClick(id)
}}
style={{...objStyle}}
/>
);
export default Item
I have a Playground here: https://codesandbox.io/s/736v9vjzw1
const Something = ({ classes, children, variant }) => {
return (
<div className={classes.someThing}>
<p> I'm some thing </p>
<SomeOtherThing />
<SomeOtherThing> I have some children </SomeOtherThing>
<SomeOtherThing> I have some children </SomeOtherThing>
<SomeOtherThing> I have some children </SomeOtherThing>
</div>
);
};
const styles = {
someThing: {
color: "green",
border: "solid 2px black",
margin: 30,
"& $someOtherThing": {
backgroundColor: "pink" // Doesn't work
},
"& p": {
fontWeight: "bold" //This works but is too broad
}
}
};
I have a situation here, where I want to style all the SomeOtherThings inside my SomeThing.
I can use & p selector to select the p element - but I don't like this. It would style any random ps I have around - and I don't want to have to look inside the component definition to find what it's top level element is.
How can I do this? Something like & SomeOtherElement.
The real world application of this, is that in some places I want have SomeOtherElement be displayed block and other places inline-block.
I would extend the SomeOtherThing component to accept a className and add it to the div if present. This will also work on a production setup, where the class names is minified to e.g. .t-0-root.
Here is a forked playground: https://codesandbox.io/s/zlzx277zzm which shows how to use it.
const SomeOtherThing = ({ classes, children, className }) => {
return (
<p className={`${classes.someOtherThing} ${className && className}`}>
I'm another thing {children}
</p>
);
};
I would most likely use the package classnames to conditionally render the class name instead of string interpolation.
const Something = ({ classes, children, variant }) => {
return (
<div className={classes.someThing}>
<p> I'm some thing </p>
<SomeOtherThing />
<SomeOtherThing className={classes.someOtherThing}>
I have some children
</SomeOtherThing>
<SomeOtherThing className={classes.someOtherThing}>
I have some children
</SomeOtherThing>
<SomeOtherThing className={classes.someOtherThing}>
I have some children
</SomeOtherThing>
</div>
);
};
const styles = {
someThing: {
color: "green",
border: "solid 2px black",
margin: 30
},
someOtherThing: {
backgroundColor: "pink"
}
};
It's so simple, in your someThing CSS codes select the p elements with class someOtherThing class name and use not() CSS operation for p in top level, see following code:
const styles = {
someThing: {
color: "green",
border: "solid 2px black",
margin: 30,
"& [class*='SomeOtherThing']": {
backgroundColor: "pink" //
},
"& :not([class*='SomeOtherThing'])": {
fontWeight: "bold" // Just for top level p
}
}
};
and
const SomeOtherThing = ({ classes, children }) => {
return (
<p className={classes.root}> I'm another thing {children} </p>
);
};
CodeSandBox
The way that this works, is that by giving SomeOtherThing any jss class, it's going to render the dom element as something like:
<p class="SomeOtherThing-root-0-1-2"> I'm another thing I have some children </p>
which the [class*='SomeOtherThing'] attribute selector will match on.
You should note that this selector will apply to any deeper nested SomeOtherThings as well.
One problem with the "cascading" aspect of CSS is that it sort of breaks React's component model. But in React you can always create a wrapper or higher-order component that returns another with some predefined props, kind of like a factory function:
const Something = props => {
return (
<div className={props.className}>
// ...
</div>
)
}
const SomethingInline = props => {
return <Something className='display-inline' {...props} />
}
const SomethingBlock = props => {
return <Something className='display-block' {...props} />
}
const App = () => {
return (
<div>
<SomethingInline />
<SomethingBlock />
<SomethingBlock> I have children </SomethingBlock>
</div>
)
}
Instead of using the & selector to define your style, create a class that only applies to these specific versions of your component. In this way, the global scope of CSS is avoided and you can create these sort of declarative components that describe their own style, but don't require you to explicitly pass a class name.
Using a common HOC pattern like so works beautifully. However, there are times when you really don't want a component to be wrapped and just want the same component you passed in extended a bit. That's what I'm struggling with here.
Wrapper HOC
const flexboxContainerStyles = {
display: 'flex',
flexDirection: 'row',
backgroundColor: 'pink',
}
let WrapInFlexContainer = FlexChild => class extends React.Component {
render(){
return (
<div className="flexContainer" style={flexboxContainerStyles} >
<FlexChild {...this.props} />
</div>
)
}
}
const Button = (props) => <button>{props.txt}</button>
let FlexButton = WrapInFlexContainer(Button);
The following examples result in a button with no style attributes.
Example 1.1: pass-through via createClass
function hocPassThroughViaClass(Component) {
return React.createClass({
render: function() {
return <Component {...this.props} style={flexboxContainerStyles}/>;
}
});
}
Example 1.2 pass-through via direct render
let hocPassThroughViaRender = Element => class extends React.Component {
render(){
return <Element {...this.props} className="flexContainer" style={flexboxContainerStyles} />
}
}
Example 2: create
function hocCreate(Component) {
return React.createClass({
render: function() {
const modifiedProps = Object.assign({}, {...this.props}, {...flexboxContainerStyles});
return React.createElement(Component, { ...modifiedProps });
}
});
}
Example 3: clone
function hocClone(Component) {
return React.createClass({
render: function() {
const modifiedProps = Object.assign({}, {...this.props}, {...flexboxContainerStyles});
return React.cloneElement(<Component {...modifiedProps } />);
}
});
}
// render examples
let HOCPassThroughViaClassButton = hocPassThroughViaClass(Button); // 1.1
let HOCPassThroughRenderButton = hocPassThroughViaRender(Button); // 1.2
let HOCCreatedButton = hocCreate(Button); // 2
let HOCClonedButton = hocClone(Button); // 3
From a couple of points I'm seeing here and there across the web, it doesn't seem like it's possible to return the same Component if it is an only child.
See: https://github.com/threepointone/glamor/blob/master/docs/createElement.md
https://discuss.reactjs.org/t/trying-to-do-a-reactdom-render-a-el-replacing-the-el-not-appending-to-it/2681/2
The following examples result in a button with no style attributes.
Isn't this happening because you're not passing the style prop along? Wouldn't this be fixed by doing this:
const Button = (props) => <button style={props.style}>{props.txt}</button>
Update:
HOCs don't magically apply props to children of the wrapped component, meaning that low level elements like <button /> or <div /> need props passed to them one way or another. You're passing props to <Button />, not <button />. You can however make an HOC that takes a basic element and adds whatever to it.
let hoc = element => (
class extends React.Component {
render() {
let { children, ...props } = this.props
return React.createElement(
element,
{ ...props, style: flexboxContainerStyles },
children,
)
}
}
)
Usage:
let FlexButton = hoc('button')
let App = props => <FlexButton>{props.txt}</FlexButton>
fiddle
That being said, you're not changing the API of the base component by passing along known props like style and className along. In fact it's a great way to make components more reusable without specifying implementation details.
// good!
let Button = ({ children, ...props }) => <button {...props}>{children}</button>