I have a ref on a component I am converting over to a styled component in my app. The ref is used to access the offsetHeight and scrollHeight properties on the raw html element of the component. Once I switched this component to a styled component, the ref now points to the styled component instead of the raw html element, and I'm unsure how to reference the base element. Can this be done?
example:
const TextArea = styled.textarea`
display: block;
margin: 0 0 0 18%;
padding: 4px 6px;
width: 64%;
font-size: 1rem;
color: #111;`;
export default class Input extends Component {
componentDidMount() {
const height = this.textInput.scrollHeight;
// do something....
}
render() {
return (
<div>
<TextArea
ref={(input) => this.textInput = input}
></TextArea>
</div>
);
}
}
Passing ref to a styled component will give you a ref to the styled-components wrapper, not the DOM node. To get a ref to actual DOM node pass the innerRef prop. (see the docs)
This is what you need to do:
const TextArea = styled.textarea``;
export default class Input extends Component {
componentDidMount() {
const height = this.textInput.scrollHeight;
// do something....
}
render() {
return (
<div>
<TextArea
innerRef={(input) => this.textInput = input}
></TextArea>
</div>
);
}
}
Yes it can be done. You can access raw html using ReactDOM.findDOMNode(). However, bear in mind that the use of this method is discouraged. You can read more about this in the referenced page.
Related
I'm trying to style a material UI component, this is my code:
export const CardContentStyled = styled(CardContent)`
width: ${props => (props.isNarrow ? 'calc(100% - 60px)' : 'calc(100% - 200px)')};
box-sizing: border-box;
position: relative;
padding: 0 60px;
`;
and then from the component I import all as ui and use it like this:
<ui.CardContentStyled isNarrow={isNarrow}>
whatever
</ui.CardContentStyled>
It works, but I'm getting this error:
Warning: React does not recognize the isNarrow prop on a DOM element. If you intentionally want it to appear in the DOM as a custom attribute, spell it as lowercase isnarrow instead. If you accidentally passed it from a parent component, remove it from the DOM element.
I tried everything I found on here, I thing the only thing that worked was this:
React does not recognize the `isActive` prop on a DOM element - styled-components
but it seems a bit hacky, maybe there's a better way to do it.
If you look at the source of card content component you will understand it passes all other props to "div" as {...others}. isNarrow is a "non default" props of the div which causes the error.
If you can live with the error, fine.
If you do not want the error, it is really not harder than to simply customise create your own component than to use CardContent with styled.
CardContent source
export const styles = {
/* Styles applied to the root element. */
root: {
padding: 16,
'&:last-child': {
paddingBottom: 24,
},
},
};
const CardContent = React.forwardRef(function CardContent(props, ref) {
/* add isNarrow below and apply your own conditional style */
const { classes, className, isNarrow, component: Component = 'div', ...other } = props;
return <Component className={clsx(classes.root, className)} ref={ref} {...other} />;
});
Im beginner on Reactjs and trying to learn and improve, here i have code where is < h1 >test< / h1 > and under this should appear numbers under each other like this 1:1 1:2 1:3, but css does not seem to work with it, i get numbers but without css and i dont get any error message either... is here something wrong ? the code :
import React, { Component } from 'react'
class Button extends Component {
state = {}
button = () => {
const proxyurl = "https://cors-anywhere.herokuapp.com/";
const url = "http://*****.*****.com/numbers.txt";
fetch(proxyurl + url)
.then(response => response.text())
.then(contents => document.write(contents))
}
render() {
return (
<div >
<h1>test</h1>
<div style={{ color: 'red' }}>{this.button()}
</div>
</div >
);
}
}
export default Button;
css:
body {
background: url('***.png');
color:red;
margin:50px 0;
padding:0px;
text-align:center;
}
#root {
white-space: pre;
}
Your render function should be pure, see https://reactjs.org/docs/react-component.html#render:
The render() function should be pure, meaning that it does not modify component state, it returns the same result each time it’s invoked, and it does not directly interact with the browser.
Your render function contains a call to this.button. So every time your component re-renders, a fetch request is made when it seems this should only be called once. As the docs suggest, move this logic into componentDidMount.
Now, onto your actual problem. You are calling document.write, and it seems you don't understand how this works. Document.write will remove all event listeners from the page and replace all content within body with the argument you supplied. Assuming you had a root element with an ID of root (<div id="root">...</div>), that will have been removed after your call to document.write; so your CSS #root selector will no longer point to an existing element.
Instead of using document.write, set the content on your component's state and render that:
import React, { Component } from "react";
export default class Button extends Component {
state = {
contents: null
};
componentDidMount() {
const proxyurl = "https://cors-anywhere.herokuapp.com/";
const url = "http://*****.*****.com/numbers.txt";
fetch(proxyurl + url)
.then(response => response.text())
.then(contents => this.setState({ contents }));
}
render() {
return (
<div>
<h1>test</h1>
<div style={{ whiteSpace: "pre" }}>{this.state.contents}</div>
</div>
);
}
}
If you're using React, you should have no reason to call document.write, even if you're doing it for testing or you're trying to implement some sort of page reload / turbolinks feature–there are much better alternatives.
I know that we can extend or add styling to existing components with styled-components like the Link component of react-router-dom. The said feature is indicated here. But, my problem is, how can I combine two or more existing components then add some more styles?
In my case, I have a reusable component for text elements like span, p and a with standard font-size, font-weight, etc. At the same time, I want to use the react-router-dom's Link component. Currently, I have something like this:
import styled from 'styled-components';
import { Link } from 'react-router-dom';
import { TextElement } from '../common';
/*
Can I do something like this?
const MyLink = styled(Link, TextElement)`
margin: 10px 0;
`;
or this?
const MyLink = styled([Link, TextElement])`
margin: 10px 0;
`;
*/
const MyPage = props => (
<>
<MyLink to="/next-page" />
</>
);
Any suggestion would be appreciated.
EDIT
My TextElement component is just something like this:
const Element = styled.span`
font-size: 13px;
font-weight: 500;
`;
// These styles are sample only. Actual styles depend on a "variant" prop.
// I did not show it here since it doesn't have to do with the question I'm asking.
export default ({ tag }) => (<Element as={tag} />);
You can use mixin for this using css helper.
Let's say you put the code for TextElement in a spaced mixin like:
import { css } from 'styled-components';
const spaced = css`
margin: 10px 0;
`;
And another mixin for Link
const styledLink = css`
color: blue;
`;
Then you can define your MyLink as:
import styled from 'styled-components'
const MyLink = styled(Link)`
${spaced}
${styledLink}
`;
The style component could be the wrapper of your custom component. For example:
Your style component:
export const CustomSpanWrapper = styled.div`
span {
...your-styles
}
`;
Your other component:
<CustomSpanWrapper>
<CustomSpan>
</CustomSpanWrapper>
a bit new to react.
I used the create react app https://github.com/facebook/create-react-app
to start a new react project.
the full code is here. https://github.com/bryandellinger/reactswitch/tree/master/src
I am trying to get the background color of a selected element to change and the text to become bold but it appears the class is never added not sure what I am doing wrong.
Switch.js
import React, { PropTypes } from 'react';
import styles from './Switch.css';
const CREDITCARD = 'Creditcard';
const BTC = 'Bitcoin';
const Choice = function (props) {
const cssClasses = [];
if (props.active) {
// <-- check props, not state
cssClasses.push(styles.active);
}
return (
<div
onClick={props.onClick}
className={cssClasses}
>
{props.label} {/* <-- allow any label */}
</div>
);
};
class Switch extends React.Component {
state = {
payMethod: BTC,
};
select = (choice) => {
return (evt) => {
this.setState({
payMethod: choice,
});
};
};
render() {
return (
<div className='switch'>
<Choice
onClick={this.select(CREDITCARD)}
active={this.state.payMethod === CREDITCARD}
label='Pay with Creditcard'
/>
<Choice
onClick={this.select(BTC)}
active={this.state.payMethod === BTC}
label='Pay with Bitcoin'
/>
Paying with: {this.state.payMethod}
</div>
);
}
}
export default Switch;
and Switch.css
.active {
background-color: #4619eb;
font-weight: bold;
}
it appears the active class from switch.css never gets added on the onclick event. not sure what I am missing.
Because of the way webpack is configured in CRA, you need to write your css like this:
:local(.active) {
background-color: #4619eb;
font-weight: bold;
}
CRA only supports importing the whole CSS file directly out of the box. So instead of importing the CSS file as a component, you would do:
import './Switch.css';
CRA docs for adding a stylesheet: https://github.com/facebook/create-react-app/blob/master/packages/react-scripts/template/README.md#adding-a-stylesheet
Also, the className property should be a string with class names separated with a while space. If you want to set the class name dynamically, check out classnames: https://github.com/JedWatson/classnames.
I am trying to implement a search bar while using the styled-components library for styling. My issue is that the queried value never changes if I used styled-components. This is my code
import styled from 'styled-components'
import React, from 'react'
const SearchBar = styled.input`
margin-top: 35px;
float: right;
`
class Header extends React.Component {
state = {
query: '',
}
handleNewQuery = () => {
this.setState({
query: this.search.value,
})
console.log(this.search.value);
}
render () {
return (
<SearchBar
placeholder='Search for...'
ref={input => this.search = input}
onChange={this.handleNewQuery}
/>
)
}
}
Which only works if I swap SearchBar with input, otherwise the log prints undefined
The base issue is the the ref that is being created is returning a StyledComponent, not an HTML input element. It simply does not have a value property. The reason it starts working when you removing the styled aspect and simply render an <input />, is then the ref is an actual HTML input element with a value property. Try logging the ref in the change event to see this with first the styled component then a standard input. Either way I'd try approaching it as a Controlled Component using value property and event.target.value instead of attempting to extract the value from a ref.
import React, { Component } from 'react';
import styled from 'styled-components';
import './style.css';
const SearchBar = styled.input`
margin-top: 35px;
float: right;
`;
class Header extends Component {
constructor() {
super();
this.state = {
query: ''
};
}
handleNewQuery = (e) => {
this.setState({
query: e.target.value
})
}
render() {
return (
<div>
<SearchBar
placeholder='Search for...'
onChange={this.handleNewQuery}
value={this.state.query}
/>
</div>
);
}
}
If you absolutely must use a ref with this styled component. You can used the property innerRef which is specific to styled components to access the underlying HTML input element. This would technically give you access the value property. Once again though, the best approach would simply be using a controlled component as described above. The below example is using the newer approach to creating refs, but it would depend on your version of React being used.
<SearchBar
placeholder='Search for...'
onChange={this.handleNewQuery}
value={this.state.query}
innerRef={this.search}
/>
Here is a StackBlitz showing the functionality in action including the innerRef.
Hopefully that helps!
SearchBar should take a value prop instead of using a ref to get the value. Something like this:
<SearchBar value={this.state.search} ... />