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>
Related
I am trying to fetch data from an API and update using form inside material UI dialog. But I have
tried all I could and the dialog will will not open.
It opens only when I comment out the line like below. Can someone help figure out what the issue is?
Why the dialog will not open until I comment out the line below.
const handleOpen = () => {
setValues({ open: true });
//mapUserData(credentials)
};
This is my code:
import { useState, useEffect } from 'react';
import { editProfile } from '../../store/actions/userActions';
import { useSelector, useDispatch } from 'react-redux';
import useStyle from './style'
import Input from '../forms/controls/Input'
import { useForm } from '../../hooks/useForm/useForm'
import Modal from 'react-modal';
//MUI Components
import Button from '#material-ui/core/Button';
import TextField from '#material-ui/core/TextField';
import Dialog from '#material-ui/core/Dialog';
import DialogActions from '#material-ui/core/DialogActions';
import DialogContent from '#material-ui/core/DialogContent';
import DialogContentText from '#material-ui/core/DialogContentText';
import DialogTitle from '#material-ui/core/DialogTitle';
import IconButton from '#material-ui/core/IconButton';
import EditIcon from '#material-ui/icons/Edit';
import Tooltip from '#material-ui/core/Tooltip';
import { Typography } from '#material-ui/core';
const EditProfile = () => {
const { handleChange, values, setValues } = useForm({
firstName: '', lastName: '', handle: '',
open: false
})
const { credentials } = useSelector((state) => state.users);
const classes = useStyle()
const mapUserData = (data) => {
setValues({
firstName: data?.firstName ? data?.firstName : '',
lastName: data?.lastName ? data?.lastName : '',
handle: data?.handle ? data?.handle : '',
});
}
const handleOpen = () => {
setValues({ open: true });
mapUserData(credentials)
};
const handleClose = () => {
setValues({ open: false });
};
useEffect(() => {
mapUserData(credentials)
}, []);
const handleSubmit = () => {
const profile = {
firstName: profile?.firstName,
lastName: profile?.lastName,
handle: profile?.handle
}
editProfile(profile)
}
return (
<div>
<IconButton onClick={handleOpen}>
<Tooltip title="Edit Profile" placement="top">
<EditIcon fontSize="small" color="inherit" className={classes.edit} />
</Tooltip>
</IconButton>
<Dialog
open={values?.open}
onClose={handleClose}
fullWidth
maxWidth="sm"
>
<DialogTitle id="form-dialog-title">Edit Profile</DialogTitle>
<DialogContent>
<form>
<Input
name="firstName"
value={values?.firstName}
type="text"
autoFocus={true}
onChange={handleChange}
/>
<Input
name="lastName"
value={values?.lastName}
type="text"
onChange={handleChange}
/>
<Input
name="handle"
value={values?.handle}
type="text"
onChange={handleChange}
/>
</form>
</DialogContent>
<DialogActions>
<Button onClick={handleClose} color="primary">
Cancel
</Button>
<Button onClick={handleSubmit} color="primary">
Save
</Button>
</DialogActions>
</Dialog>
</div>
);
};
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} />
}
Trying to educate in React. It might be the case, that the whole structure is wrong however, this is it:
LoginComponent contains LoginForm, and passes onSubmit event down to the form. The submitForm triggers an actionCreator, that is why I have to use this.props.login. But when the call is happening this is undefined. I'm doing this, since LoginComponent will become an Auth component and contain the registration form as well. But is it correct in general?
import React from 'react';
import {connect} from "react-redux";
import {userActions} from "../../actions/auth.actions";
import LoginForm from "./loginForm";
class LoginComponent extends React.Component {
constructor(props) {
super(props);
this.state = {
username: '',
password: ''
}
}
submitForm(username, password) {
this.props.login(username, password);
}
render() {
return (
<>
<LoginForm onSubmit={this.submitForm}/>
</>
);
}
}
const mapStateProps = state => {
const {isLoggedIn, isLoggingIn, user} = state;
return {isLoggedIn, isLoggingIn, user};
};
const actionCreators = {
login: userActions.login,
};
const connectedLoginComponent = connect(mapStateProps, actionCreators)(LoginComponent);
export {connectedLoginComponent as Login};
LoginForm:
import React, {useState} from 'react';
import PropTypes from 'prop-types';
const LoginForm = (props) => {
const [username, setUsername] = useState('');
const [password, setPassword] = useState('');
const { onSubmit } = props;
const handleSubmit = (e) => {
e.preventDefault();
onSubmit(username, password);
};
return (
<>
<form onSubmit={e => handleSubmit(e)}>
<input
onChange={e => setUsername(e.target.value)}
value={username}
name={"username"}
type="text"
placeholder={'Username'}
required
/>
<input
onChange={e => setPassword(e.target.value)}
value={password}
name={"password"}
type="text"
placeholder={'Password'}
required
/>
<button>Login</button>
</form>
</>
);
};
LoginForm.propTypes = {
onSubmit: PropTypes.func,
};
export default LoginForm;
The function is not having this access we have to explicitly bind this to function or we can use arrow function.
Arrow function:
submitForm = (username, password) => {
this.props.login(username, password);
}
Bind keep this in your constructor :
this.submitForm = this.submitForm.bind(this)
import React from 'react';
import {connect} from "react-redux";
import {userActions} from "../../actions/auth.actions";
import LoginForm from "./loginForm";
class LoginComponent extends React.Component {
constructor(props) {
super(props);
this.state = {
username: '',
password: ''
}
}
submitForm =(username, password) => this.props.login(username, password);
render() {
return (
<>
<LoginForm onSubmit={this.submitForm}/>
</>
);
}
}
const mapStateProps = state => {
const {isLoggedIn, isLoggingIn, user} = state;
return {isLoggedIn, isLoggingIn, user};
};
const actionCreators = {
login: userActions.login,
};
const connectedLoginComponent = connect(mapStateProps, actionCreators)(LoginComponent);
export {connectedLoginComponent as Login};
and Login.jsx :
import React, {useState} from 'react';
import PropTypes from 'prop-types';
const LoginForm = ({onSubmit}) => {
const [username, setUsername] = useState("");
const [password, setPassword] = useState("");
const handleSubmit = (e) => {
e.preventDefault();
onSubmit(username, password);
};
return (
<>
<form onSubmit={e => handleSubmit(e)}>
<input
onChange={e => setUsername(e.target.value)}
value={username}
name={"username"}
type="text"
placeholder={'Username'}
required
/>
<input
onChange={e => setPassword(e.target.value)}
value={password}
name={"password"}
type="text"
placeholder={'Password'}
required
/>
<button>Login</button>
</form>
</>
);
};
LoginForm.propTypes = {
onSubmit: PropTypes.func,
};
export default LoginForm;
My Login.js file was getting larger and larger so I decided to create a reusable component in a separate file, LoginForm.js. However, after I extracted the previous code from Login.js into LoginForm.js one of my props is getting undefined and I have no idea why.
The prop who's is getting undefined: this.props.token
I've imported all the modules and compared the code, and everything seems to be correct.
File 1
In Login.js I have this:
import React, { Component } from 'react'
import { connect } from 'react-redux'
import { createToken } from '../../actions/tokenActions'
import LoginForm from './LoginForm'
// Styles
import Typography from 'material-ui/Typography'
import Card, { CardActions, CardContent } from 'material-ui/Card'
import Button from 'material-ui/Button'
import { CircularProgress } from 'material-ui/Progress'
import injectSheet from 'react-jss'
class Login extends Component {
constructor(props) {
super(props)
this.state = {}
}
onFormSubmit(e) {
e.preventDefault()
this.props.createToken(this.state)
}
render() {
const progressBar = this.props.token.pending
? <CircularProgress className={this.props.progress} />
: ''
return (
<div className="box" style={{ margin: '100px auto', width: '300px' }}>
{progressBar}
<form
onSubmit={this.onFormSubmit.bind(this)}
style={{ display: this.props.token.pendling ? 'none' : 'block' }}
>
<LoginForm />
</form>
</div>
)
}
}
const mapState = state => {
return {
token: state.token
}
}
const mapDispatch = (dispatch, props) => ({
createToken: data => dispatch(createToken(data))
})
export default connect(mapState, mapDispatch)(Login)
File 2
And in my other file, LoginForm.js I have this:
import React, { Component } from 'react'
import { connect } from 'react-redux'
import { createToken } from '../../actions/tokenActions'
//Styles
import Card, { CardActions, CardContent } from 'material-ui/Card'
import Button from 'material-ui/Button'
import Typography from 'material-ui/Typography'
export default class LoginForm extends Component {
constructor(props) {
super(props)
this.state = {}
}
onFormSubmit(e) {
e.preventDefault()
this.props.createToken(this.state)
}
render() {
console.log(this.props.token)
return (
<Card>
<CardContent>
<Typography
style={{
fontSize: '40px',
fontWeight: '300',
color: '#636363',
marginBottom: '20px'
}}
type="subheading"
gutterBottom
align="center"
>
Logga In
</Typography>
<input
type="text"
placeholder="Email"
onChange={e => this.setState({ email: e.target.value })}
/>
<input
type="password"
placeholder="Lösenord"
onChange={e => this.setState({ password: e.target.value })}
/>
<Button
raised
color="primary"
onClick={this.onFormSubmit.bind(this)}
className="text-center"
>
Skicka
</Button>
{this.props.token.rejected
? 'Felaktigt användarnamn eller lösenord'
: ''}
</CardContent>
</Card>
)
}
}
I bet it's a really basic question, but I have no other to ask than here. I am really grateful for all the help!
<LoginForm /> doesnt provide any props.
this.props wouldn't be undefined if you provided it like this: <LoginForm token={this.props.token} /> props doesnt get passed on if you dont explicit write it, you can write them one by one but you can also type like this: <LoginForm {...this.props} />
I don't understand how can I access to form props. I used formValueSelector as suggested the documentation, but doesn't work. Where is my mistake? I'm using the latest version of redux-form.
LoginForm.js
'use strict';
import React from 'react';
import { Field, reduxForm, formValueSelector } from 'redux-form';
import { TextInput, View, TouchableHighlight, Text } from 'react-native';
import { connect } from 'react-redux';
class LoginForm extends React.Component {
handeSubmit(email, password){ alert(`email: ${email} and password: ${password}`)};
render() {
return (
<View>
<Field
name="email"
component={TextInput}
placeholder="Email"
/>
<Field
name="password"
component={TextInput}
placeholder="Password"
secureTextEntry={true}
/>
<TouchableHighlight onPress={() => this.handeSubmit(this.props.email, this.props.password)}>
<Text>Submit</Text>
</TouchableHighlight>
</View>
);
}
}
LoginForm = reduxForm({
form: 'loginForm'
})(LoginForm);
const selector = formValueSelector('loginForm');
function mapStateToProps(state){
return {
email: selector(state, 'email'),
password: selector(state, 'password'),
}
}
LoginForm = connect(mapStateToProps)(LoginForm);
export default LoginForm;
LoginPage.js
'use strict';
import React from 'react';
import {View} from 'react-native';
import Container from '../../components/Container';
import LoginForm from '../../components/forms/LoginForm';
class LoginPage extends React.Component {
render() {
return (
<Container>
<LoginForm/>
</Container>
);
}
}
export default LoginPage;
Result:
I use alert(this.props) onPress button and this is the output. There aren't email and password.
and this is the output for
<TouchableHighlight onPress={() => alert(JSON.stringify(test))}>
<Text>Submit</Text>
</TouchableHighlight>
function mapStateToProps(state){
return {
test: state.form.loginForm
}
}
I found the solution. TextInput doesn't work with redux-form as it is. It need a component than pass the redux-form props like this:
TextField.js
class TextField extends React.Component {
render(){
const { input: { value, onChange } } = this.props; <----add this!!!
return (
<TextInput
style={[styles.textInput]}
onChangeText={(value) => onChange(value)} <----add this!!!
add this!!!---> value={value} underlineColorAndroid="transparent" selectTextOnFocus={true} {...this.props}
/>
);
}
}
Instead of import TextInput inside the forms, import the custom TextField.