I am trying to create a reusable component where I have redux-form <Field /> returned and in this component I am styling it with styled-component.
the challenge I have is that none of the style is reflecting
this is my simple-field-input.styles.ts
import React from 'react';
import { Field } from 'redux-form';
import styled from 'styled-components';
import { SimpleFieldProps } from './simple-field-input.type';
const ReduxFormField: React.FC<SimpleFieldProps> = ({ componentType }) => {
return <Field component={componentType} name="email" />;
};
export const Container = styled(ReduxFormField)`
outline: none;
border: none;
background-color: orangered;
color: yellow;
`;
and here is my simple-field-input.component.tsx
import React from 'react';
import * as Style from '../simple-field-input/simple-field-input.styles';
import { SimpleFieldProps } from './simple-field-input.type';
const FieldInput: React.FC<SimpleFieldProps> = ({ componentType }) => {
return <Style.Container componentType={componentType}></Style.Container>;
};
export default FieldInput;
it simple renders the input but not implementing the styles...
i will appreciate any help. thanks
When using styled on on component which is not a DOM element, what it does is add a className prop to the component. The component needs to access that className and pass it through to a DOM element.
You're actually passing it down twice here, since the className should end up on the componentType rather than the Field itself.
const ReduxFormField: React.FC<SimpleFieldProps & {className?: string}> = (
{ componentType, className }
) => {
return <Field component={componentType} props={{className}} name="email" />;
};
Related
I am using React + Material UI + Styled Component. I am trying to create a custom Input field. I want to pass always size='small' as props to my component.
In other words, if the user forgets to pass size, it always takes small. Or if the user by mistake passes any other value other than small, still it always takes 'small' only.
Here is my code:
https://codesandbox.io/s/awesome-meadow-utv9bc?file=/src/App.tsx
import * as React from "react";
import TextField, { TextFieldProps } from "#mui/material/TextField";
import { styled } from "#mui/material/styles";
const Input = styled(TextField)<TextFieldProps>(
({ disabled, multiline, theme }) => {
return {
border: `1px solid red`
};
}
);
export default Input;
I am using like this
<Input label="Outlined" />
I want to pass size always small Any idea?
I suppose a simple solution would be to create a component that overrides the size prop to always be "small" and then style that.
import TextField, { TextFieldProps } from "#mui/material/TextField";
import { styled } from "#mui/material/styles";
const SmallTextField = (props: TextFieldProps) => (
<TextField {...props} size="small" />
);
const Input = styled(SmallTextField)<TextFieldProps>(
({ disabled, multiline, theme }) => {
return {
border: `1px solid red`
};
}
);
export default Input;
Use Mui Theme Provider
import * as React from "react";
import { createTheme, ThemeProvider } from "#mui/material/styles";
import TextField from "#mui/material/TextField";
const theme = createTheme({
components: {
// Name of the component ⚛️
MuiTextField: {
defaultProps: {
size: "small"
}
}
}
});
export default function DefaultProps() {
return (
<ThemeProvider theme={theme}>
<TextField />
</ThemeProvider>
);
}
You could use a wrapper that would set size="small" and remove size from the props with Omit, so anyone who uses your Input knows he cannot set it with the help of TypeScript errors:
import TextField, { TextFieldProps } from "#mui/material/TextField";
import { styled } from "#mui/material/styles";
const InnerInput = styled(TextField)<TextFieldProps>((props) => {
return {
...props
};
});
const Input = (props: Omit<TextFieldProps, "size">) => (
<InnerInput {...props} size="small" />
);
export default Input;
I have a react component that looks like this:
import { TextareaHTMLAttributes} from 'react'
import styled from 'styled-components'
const TextAreaElement = styled.textarea`
border-radius: 40px;
border: none;
background: white;
`
const TextArea = (props: TextareaHTMLAttributes<any>) => { <--- replace <any> here
return <TextAreaElement {...props} />
}
I know I can do something like this, but would rather not have to add every prop manually:
const TextArea = ({placeholder} : {placeholder: string}) => {
return <TextAreaElement placeholder={placeholder} />
}
You can pass the props as regular HTML element
import React from "react";
const CustomTA = (props: React.HTMLProps<HTMLTextAreaElement>) => {
return <textarea {...props} />;
};
I'm new in React.JS, I'm currently using it with Node and I'm having the following issue:
I'm using a button from Material-UI (Google's interface assets - buttons, menus, etc) and I have the button's styles defined inside a const that is ran through a function (because it's how it is declared in their own website, I'm not sure why I need to have a function instead of just calling the const).
What I'm doing is customizing the margin of the button I'm importing and setting the display to 'none' on the browser's pre-defined button so it disappears.
I inserted the const with the styles values inside a component called HookApi.js and this is its content:
import React, { useState } from 'react';
import '../App.css';
import { makeStyles } from '#material-ui/core/styles';
import Button from '#material-ui/core/Button';
const useStyles = makeStyles((theme) => ({
root: {
'& > *': {
margin: theme.spacing(1),
},
},
input: {
display: 'none',
},
}));
export function UploadButtons() {
const classes = useStyles();
}
And this is what I have inside my Gallery.js (which is replacing my App.js for testing purposes)
import React, { useState } from 'react';
import './App.css';
import { makeStyles } from '#material-ui/core/styles';
import Button from '#material-ui/core/Button';
import HookApi from './constants/HookApi';
import { classes } from './constants/HookApi';
class Gallery extends React.Component{
constructor(props) {
super(props);
}
render() {
return(
<div className={classes.root}>
<input
accept="image/*"
className={classes.input}
id="contained-button-file"
multiple
type="file"
/>
<label htmlFor="contained-button-file">
<Button variant="contained" color="primary" component="span">
Upload
</Button>
</label>
</div>
);
}
}
export default Gallery;
I'm not sure if I'm importing the const the wrong way in my Gallery.js but it gives me the following error when I'm rendering the page
'classes' is not exported from 'HookApi' - image
Instead of exporting a const, you can try to export the function.
Gallery.js
import UploadButtons from './constants/HookApi';
render() {
const {input} = this.props.UploadButtons();
//...other logic
(<input
accept="image/*"
className={input}
id="contained-button-file"
multiple
type="file"
/>)
}
}
I am trying to programatically focus TextInput element with a certain delay after it has mounted, I've got following component (a view that shows input and button)
For some reason this gets error saying
_this2.inputRef.focus is not a function
I'm not sure why. One out of place thing is that I get flow saying that createRef() doesn't exist on React, but I am assuming this is just missing flow definition now, as I am using react 16.3.1 and this was added in 16.3, plus there is no error when its called.
// #flow
import React, { Component, Fragment } from 'react'
import Button from '../../composites/Button'
import TextInput from '../../composites/TextInput'
import OnboardingStore from '../../store/OnboardingStore'
import withStore from '../../store'
import { durationNormal } from '../../services/Animation'
/**
* Types
*/
export type Props = {
OnboardingStore: OnboardingStore
}
/**
* Component
*/
class CharacterNameView extends Component<Props> {
componentDidMount() {
this.keyboardTimeout = setTimeout(() => this.inputRef.focus(), durationNormal)
}
componentWillUnmount() {
clearTimeout(this.keyboardTimeout)
}
keyboardTimeout: TimeoutID
inputRef = React.createRef()
render() {
const { OnboardingStore } = this.props
return (
<Fragment>
<TextInput
ref={this.inputRef}
enablesReturnKeyAutomatically
value={OnboardingStore.state.username}
onChangeText={username => OnboardingStore.mutationUsername(username)}
placeholder="Username"
blurOnSubmit
returnKeyType="done"
onSubmitEditing={/* TODO */ () => null}
/>
<Button disabled={!OnboardingStore.state.username} color="GREEN" onPress={() => null}>
Create
</Button>
</Fragment>
)
}
}
export default withStore(OnboardingStore)(CharacterNameView)
TextInput component I use is imported from this file
// #flow
import React, { Component } from 'react'
import { StyleSheet, TextInput as Input } from 'react-native'
import RatioBgImage from '../components/RatioBgImage'
import { deviceWidth } from '../services/Device'
/**
* Types
*/
export type Props = {
style?: any
}
/**
* Component
*/
class TextInput extends Component<Props> {
static defaultProps = {
style: null
}
render() {
const { style, ...props } = this.props
return (
<RatioBgImage source={{ uri: 'input_background' }} width={70} ratio={0.1659}>
<Input
{...props}
placeholderTextColor="#4f4a38"
selectionColor="#797155"
autoCapitalize="none"
autoCorrect={false}
keyboardAppearance="dark"
style={[styles.input, style]}
/>
</RatioBgImage>
)
}
}
export default TextInput
/**
* Styles
*/
const styles = StyleSheet.create({
input: {
width: '96.4%',
height: '97.45%',
color: '#797155',
fontSize: deviceWidth * 0.043,
marginLeft: '1%',
paddingLeft: '5%'
}
})
Below is what this.innerRef looks like, I don't see any focus property on it at the moment
inputRef is a property of the class instance, set it inside a class method.
Using a constructor, for instance:
class CharacterNameView extends Component<Props> {
constructor() {
this.inputRef = React.createRef()
}
...
}
My issue was in using React.createRef() and assuming I am setting it on a child component. Unfortunately I missed part of the docs about React.forwardRef That I had to use in a child component so ref gets set properly.
How can I access the ThemeProvider props in global.js when using styled-components?
For example in theme.js I have ${props => props.theme.fonts.fontSize} calling a default font size of 16px
const theme = {
fonts: {
fontSize : '16px',
}
}
export default theme
This is provided in /layouts/index.js as
import React from 'react'
import { ThemeProvider } from 'styled-components'
import '../style/global';
import theme from '../style/theme'
class Template extends React.Component {
render() {
const { children } = this.props
return (
<ThemeProvider theme={theme}>
...
{children()}
...
</ThemeProvider>
)
}
}
export default Template
From here I can access the ${props => props.theme.fonts.fontSize} within each component or child page.
But how can I pass to global.js in the same way when global is technically a level above theme.js? So that I could create a global style as
injectGlobal`
html {
font-size: (${props => props.theme.fonts.fontSize} / 16px) * 1em;
}
`
The easiest way off solving this is by creating a top level component that injects your desired styling like this:
import { Children } from 'react';
import { withTheme, injectGlobal } from 'styled-components';
const GlobalComponent = ({ theme, children }) => {
injectGlobal`
font-size: ${theme.fonts.fontSize}
}
`;
return Children.only(children);
};
export default withTheme(Global);
This will make sure all Components that have this Component as a parent will have the desired globalStyling. Hope this helped
Late but now we can actually create a Global Component and pass it as a child of ThemeProvider. It will allow you to access all the props of current theme.
Example for applying font family:
Your Global.js / Global.ts
import { createGlobalStyle } from "styled-components";
const GlobalStyle = createGlobalStyle`
html,
body {
padding: 0;
margin: 0;
font-family: ${(props) => props.theme.font.family}
}
a {
color: inherit;
text-decoration: none;
}
* {
box-sizing: border-box;
}
`;
export default GlobalStyle;
Your Main component app.tsx / app.jsx
import theme...
import { ThemeProvider } ...
imort GlobalStyle from '../path-to-global-file';
const App ...
.
.
return(
<>
<ThemeProvider theme={theme}>
<GlobalStyle />
{ /* Root component */ }
<Component/>
</ThemeProvider>
</>
);
You can use the props easily now.