I'd like to supply a material UI TextField component to the PhoneInput component from react-phone-number-input as the inputComponent prop.
However, I don't seem to be able to successfully apply the ref. Although I see the Material UI TextField component rendered to the UI and state is successfully updated with the value, it keeps loosing focus after the first value has been typed.
import React, { forwardRef, createRef } from 'react';
import { TextField } from '#material-ui/core';
import 'react-phone-number-input/style.css';
import PhoneInput from 'react-phone-number-input';
const SampleComponent = ({ handleChange }) => {
const phoneInput = forwardRef((props, ref) => {
return (
<TextField
inputRef={ref}
fullWidth
label="Phone Number"
variant="outlined"
name="phone"
onChange={handleChange}
/>
);
});
const ref = createRef();
return (
<PhoneInput ref={ref} inputComponent={phoneInput} />
);
};
So I was able to get it to work using the following method. Any suggestions on how to improve this are more than welcome.
I have a separate file called PhoneNumber.jsx
import { forwardRef } from 'react'
import TextField from '#material-ui/core/TextField'
import { makeStyles } from '#material-ui/core/styles'
const useStyles = makeStyles(theme => ({
input: {
backgroundColor: '#fff'
}
}))
const phoneInput = (props, ref) => {
const classes = useStyles()
return (
<TextField
{...props}
InputProps={{
className: classes.input
}}
inputRef={ref}
fullWidth
size='small'
label='Phone Number'
variant='outlined'
name='phone'
/>
)
}
export default forwardRef(phoneInput)
And my form file:
import PhoneInput from 'react-phone-number-input'
import CustomPhoneNumber from '../components/prebuilt/PhoneNumber'
...
<PhoneInput
placeholder='Enter phone number'
value={phone}
onChange={setPhone}
inputComponent={CustomPhoneNumber}
/>
...
There is also a package for Material UI v5 (or MUI) called Mui tel input and it's working with React 17 and 18 !
Simply way to use. Phone validation available too.
Check the doc here : https://github.com/viclafouch/mui-tel-input
import React from 'react'
import { MuiTelInput } from 'mui-tel-input'
const MyComponent = () => {
const [value, setValue] = React.useState('')
const handleChange = (newValue) => {
setValue(newValue)
}
return <MuiTelInput label="Phone" fullWidth value={value} onChange={handleChange} />
}
Related
Ive downloaded a DarkModeToggle npm for my react app however I am a bit confused as to actually add the functionallity. Currently the button lets me click it on the appbar and the button itself changes however the state of my app does not.
import React, { useState, useEffect } from "react";
import { Container, AppBar, Typography, Grow, Grid } from "#material-ui/core";
import { useDispatch } from "react-redux";
import DarkModeToggle from "react-dark-mode-toggle";
// import { getPosts } from './actions/posts'
import Posts from "./components/Posts/Posts";
import Form from "./components/Form/Form";
import wisp_logo from "./images/wisp_logo.png";
import useStyles from "./styles";
const App = () => {
const [currentId, setCurrentId] = useState();
const classes = useStyles();
const dispatch = useDispatch();
const [isDarkMode, setIsDarkMode] = useState(() => false);
return (
<Container maxwidth="lg">
<AppBar className={classes.appBar} position="static" color="inherit">
<DarkModeToggle
onChange={setIsDarkMode}
checked={isDarkMode}
size={80}
/>
</AppBar>
</Container>
);
};
export default App;
If you wish to customize the theme, you need to use the ThemeProvider component in order to inject a theme into your application. Here's a simple example:
Custom variables:
const darkTheme = createTheme({
palette: {
type: "dark"
}
});
Use the ThemeProvider component:
<ThemeProvider theme={darkTheme}>
<AppBar
position="static"
color={`${isDarkMode ? "default" : "primary"}`}
>
<DarkModeToggle
onChange={setIsDarkMode}
checked={isDarkMode}
size={80}
/>
</AppBar>
</ThemeProvider>
Using a ternary to change the theme:
color={`${isDarkMode ? "default" : "primary"}`}
Exemplo completo aqui: https://codesandbox.io/s/holy-wood-cdgpks?file=/src/App.js
I believe there are 2 things to change:
The first one is that "isDarkMode" is a boolean, and inside the useState you're using a function, so must be like this:
const [isDarkMode, setIsDarkMode] = useState(false);
Also, when sending the "setIsDarkMode" function you need to update your state, like this:
<DarkModeToggle
onChange={() => setIsDarkMode(prevState => !prevState)}
checked={isDarkMode}
size={80}
/>
So "onChange" is now a function that is going to update the state on every click
I have a react component that takes the input from a materialUi text form and uses a useState hook to update. On submit I want it to update another variable to the value. This is being used in an h1 element. However, the h1 element is not updating. I know the variable is updating because I log it in the console and it updates.
import React, { useState } from 'react';
import CssBaseline from '#mui/material/CssBaseline';
import { ThemeProvider, createTheme } from '#mui/material/styles';
import Alert from '#mui/material/Alert';
import { TextField } from '#mui/material';
import Button from '#mui/material/Button';
import './style.css';
import { useEffect } from 'react';
const darkTheme = createTheme({
palette: {
mode: 'dark',
},
});
export default function App() {
const [myValue, setValue] = useState('') // The original hook
let headingText = 'Hello World'
let onSubmit = (e) => {
e.preventDefault();
headingText = myValue;
console.log(headingText);
console.log(myValue);
};
return (
<>
<title>Text Changer</title>
<div className='joe'>
<ThemeProvider theme={darkTheme}>
<React.Fragment>
<CssBaseline />
<Alert severity="success">Page Loaded</Alert>
<h1>{headingText}</h1> // the h1 I want to update
<form onSubmit={onSubmit}>
<TextField id="outlined-basic" label="Search Text" variant="outlined" value={myValue} onChange={(e) => setValue(e.target.value)}/>
<br />
<br />
<Button variant="contained" color="primary" type="submit">Submit</Button>
</form>
</React.Fragment>
</ThemeProvider>
</div>
</>
);
}
You'll want to useState for that as well
const [headingText, setHeadingText] = useState('Hello World');
// ...
setHeadingText(myValue);
You have to create a state as
const [headingText, setHeadingText] = useState("Hello World");
CODESANDBOX DEMO.
and then onClick of a button you have to update state. React will re-render the component if the state changes
let onSubmit = (e) => {
e.preventDefault();
setHeadingText(myValue);
};
All I want to achieve is a page that has a header that reads: "Survey Start" and then a user select interface under that. I am envisioning a very simple component:
export class Survey extends Component {
state = {
}
render() {
return (
<React.Fragment>
<h2>Survey Start</h2>
<Survey sorcery here>
</React.Fragment>
)
}
Then my App.js runs this.
I aim to replace "survey sorcery" with the "some important helper text" version of MaterialUI's user select. I directly copied from the javascript source that was provided. Seen here:
https://material-ui.com/components/selects/
I thought it would be as simple as copying and pasting within my component:
import React, { Component } from 'react';
import { makeStyles } from '#material-ui/core/styles';
import InputLabel from '#material-ui/core/InputLabel';
import MenuItem from '#material-ui/core/MenuItem';
import FormHelperText from '#material-ui/core/FormHelperText';
import FormControl from '#material-ui/core/FormControl';
import Select from '#material-ui/core/Select';
export class Survey extends Component {
state = {
}
const useStyles = makeStyles((theme) => ({
formControl: {
margin: theme.spacing(1),
minWidth: 120,
},
selectEmpty: {
marginTop: theme.spacing(2),
},
}));
export default function SimpleSelect() {
const classes = useStyles();
const [age, setAge] = React.useState('');
const handleChange = (event) => {
setAge(event.target.value);
};
}
render() {
return (
<React.Fragment>
<h2>Survey Start</h2>
<FormControl className={classes.formControl}>
<InputLabel id="demo-simple-select-helper-label">Age</InputLabel>
<Select
labelId="demo-simple-select-helper-label"
id="demo-simple-select-helper"
value={age}
onChange={handleChange}
>
<MenuItem value="">
<em>None</em>
</MenuItem>
<MenuItem value={10}>Ten</MenuItem>
<MenuItem value={20}>Twenty</MenuItem>
<MenuItem value={30}>Thirty</MenuItem>
</Select>
<FormHelperText>Some important helper text</FormHelperText>
</FormControl>
</React.Fragment>
);
}
}
However this returned several errors, including citing the line where I defined useStyles:
The 'const' modifier can only be used in Type Script files.
Not sure why it threw that since I was careful to click the javascript example file on the example source code.
Question
How do I get the materialUI example user select to integrate with a vanilla component?
You are mixing components inside components.
Try to see thinking in react to get a better understanding of how component works. You need to export <SimpleSelect /> as its own component, then use it in your <Survey /> component.
Refactored code:
import React, { Component } from 'react';
import { makeStyles } from '#material-ui/core/styles';
import InputLabel from '#material-ui/core/InputLabel';
import MenuItem from '#material-ui/core/MenuItem';
import FormHelperText from '#material-ui/core/FormHelperText';
import FormControl from '#material-ui/core/FormControl';
import Select from '#material-ui/core/Select';
const useStyles = makeStyles(theme => ({
formControl: {
margin: theme.spacing(1),
minWidth: 120
},
selectEmpty: {
marginTop: theme.spacing(2)
}
}));
function SimpleSelect() {
const classes = useStyles();
const [age, setAge] = React.useState('');
const handleChange = event => {
setAge(event.target.value);
};
return (
<FormControl className={classes.formControl}>
<InputLabel id="demo-simple-select-helper-label">Age</InputLabel>
<Select
labelId="demo-simple-select-helper-label"
id="demo-simple-select-helper"
value={age}
onChange={handleChange}
>
<MenuItem value="">
<em>None</em>
</MenuItem>
<MenuItem value={10}>Ten</MenuItem>
<MenuItem value={20}>Twenty</MenuItem>
<MenuItem value={30}>Thirty</MenuItem>
</Select>
<FormHelperText>Some important helper text</FormHelperText>
</FormControl>
);
}
export class Survey extends Component {
state = {};
render() {
return (
<>
<h2>Survey Start</h2>
<SimpleSelect />
</>
);
}
}
In my React (v16.3) app I am rendering date-picker controls using material-ui-pickers library's DatePicker component. This component renders a Material-UI TextField component. I like to change this so it renders only a Material-UI Input without the chrome rendered by TextField.
As I understood one can to this with the DatePickers TextFieldComponent field (here at the bottom) but I couldn't figure out how to use this field.
<DatePicker
id={name}
TextFieldComponent={...<HOW_TO> ...}
value={value}
onChange={this.handleChange}
disabled={isReadOnly} />
Any ideas how to do this?
Update:
Got one step further by finding the right syntax to use and not its rendering without the chrome (FormControl, InputLabel, etc.). But also no DatePicker is opened anymore. Do I have to open it programmatically?
<DatePicker
id={name}
TextFieldComponent={(props) => this.renderInput(props)}
value={value}
onChange={this.handleChange}
disabled={isReadOnly} />
And here is renderInput():
renderInput(props: TextFieldProps): any {
return ( <Input
id={props.id}
value={props.value}
onChange={this.handleChange}
type={'text'}
disabled={props.disabled}
/> );
}
Updated October 2020
#material-ui/pickers v3.x (latest)
To render an Input instead of a TextField component, you can pass a custom component to the TextFieldComponent prop on DatePicker.
You will need to pass along the following props to the Input: value, onChange, and onClick.
import React, { useState } from "react";
import { Input, TextFieldProps } from "#material-ui/core";
import { DatePicker } from "#material-ui/pickers";
import { MaterialUiPickersDate } from "#material-ui/pickers/typings/date";
export default function Demo() {
const [selectedDate, setSelectedDate] = useState<MaterialUiPickersDate>(
new Date()
);
const renderInput = (props: TextFieldProps): any => (
<Input
type="text"
onClick={props.onClick}
value={props.value}
onChange={props.onChange}
/>
);
return (
<DatePicker
label="Basic example"
value={selectedDate}
onChange={setSelectedDate}
TextFieldComponent={renderInput}
/>
);
}
#material-ui/pickers v4.0.0-alpha12 (next)
NOTE: v4 is currently in alpha, so the API might change in the future.
In V4, you use the renderInput prop on DatePicker to customize the input (see docs). The following example renders a material ui Input instead of a TextField.
import React, { useState } from "react";
import { Input, TextFieldProps } from "#material-ui/core";
import { DatePicker } from "#material-ui/pickers";
export default function Demo() {
const [selectedDate, setSelectedDate] = useState<Date | null>(new Date());
const renderInput = (props: TextFieldProps): any => (
<Input
type="text"
inputRef={props.inputRef}
inputProps={props.inputProps}
value={props.value}
onClick={props.onClick}
onChange={props.onChange}
endAdornment={props.InputProps?.endAdornment}
/>
);
return (
<DatePicker
label="Basic example"
value={selectedDate}
onChange={setSelectedDate}
renderInput={renderInput}
/>
);
}
I want to use material-ui with reactjs. When I want to render a FlatButton, I get this error: TypeError: _context$muiTheme is undefined
I'm new using reactjs and I do not know what could be the error. Here is my code:
import React, {Component} from 'react'
import { Alert, Button, Jumbotron, Form } from 'reactstrap';
import FlatButton from 'material-ui/FlatButton';
import TextInput from './TextInput'
export default class LoginForm extends Component {
state = {
username: '',
password: ''
}
handleInputChange = (event) => {
const target = event.target;
const value = target.type ===
'checkbox' ? target.checked : target.value;
const name = target.name;
this.setState({
[name]: value
});
}
componentDidMount() {
this.primaryInput.focus();
}
onSubmit = (event) => {
event.preventDefault()
this.props.onSubmit(this.state.username, this.state.password)
}
render() {
const errors = this.props.errors || {}
return (
<Form onSubmit={this.onSubmit}>
{errors.non_field_errors?<Alert color="danger">{errors.non_field_errors}</Alert>:""}
<TextInput name="username" label="Username" error={errors.username} getRef={input => this.primaryInput = input} onChange={this.handleInputChange}/>
<TextInput name="password" label="Password" error={errors.password} type="password" onChange={this.handleInputChange}/>
<FlatButton label="Primary" type="submit" primary={true} />
</Form>
)
}
}
Any ideas?
Thanks.
The docs say that you need to use the MuiThemeProvider as a wrapper over your app.
For example :
import MuiThemeProvider from 'material-ui/styles/MuiThemeProvider';
import FlatButton from 'material-ui/FlatButton';
<MuiThemeProvider>
<FlatButton label="Primary" type="submit" primary={true} />
</MuiThemeProvider>