The error seems common and when I made some research I saw that it's mostly due to omission of middlewere (like thunk) or failing to call the dispatch function. Even after trying to put those things in check I keep getting the error
Redux Action
export const signup = (username, email, password, re_password) => async dispatch => {
const config = {
headers: {
'Content-Type': 'application/json'
}
};
const body = JSON.stringify({ username, email, password, re_password });
try {
const {data} = await axios.post(`${process.env.REACT_APP_API_URL}/api/users/`, body, config);
console.log(body)
dispatch({
type: SIGNUP_SUCCESS,
payload: data
});
} catch (error) {
dispatch({
type: SIGNUP_FAIL,
payload: error.response && error.response.data.detail
? error.response.data.detail
: error.message,
})
}
};
function RegisterScreen({ signup, isAuthenticated }) {
const [accountCreated, setAccountCreated] = useState(false);
const [username, setUsername] = useState([])
const [email, setEmail] = useState([])
const [password, setPassword] = useState([])
const [re_password, setRe_password] = useState([])
const [message, setMessage] = useState('')
const dispatch = useDispatch()
const auth = useSelector(state => state.auth)
const { error, loading } = auth
const submitHandler = (e) => {
e.preventDefault();
if (password !== re_password) {
setMessage('Both passwords must be the same')
} else {
dispatch(signup(username, email, password, re_password));
setAccountCreated(true);
}
}
return (
<Container className='content auth-container'>
<div className="auth-header text-center mb-4">
<h2 className="auth-header">Sign Up</h2>
<p>Add your deatils to sign up</p>
</div>
{message && <Message variant='danger'>{message}</Message>}
{error && <Message variant='danger'>{error}</Message>}
{loading && <Loader />}
<Form className="auth-form" onSubmit={submitHandler}>
<Form.Group className="mb-3" controlId='name'>
<Form.Control
className="auth-input search-ppty"
required
minLength='6'
type="name"
placeholder="Username"
value={username}
onChange={(e) => setUsername(e.target.value)}
/>
</Form.Group>
<Form.Group className="mb-3" controlId='email'>
<Form.Control
required
className="auth-input search-ppty"
type="email"
placeholder="Email"
value={email}
onChange={(e) => setEmail(e.target.value)}
/>
</Form.Group>
<Form.Group className="mb-3" controlId="password">
<Form.Control
className="auth-input search-ppty"
type="password"
placeholder="Password"
value={password}
onChange={(e) => setPassword(e.target.value)}
/>
</Form.Group>
<Form.Group className="mb-3" controlId="passwordConfirm">
<Form.Control
className="auth-input search-ppty"
type="password"
placeholder="Confirm Password"
value={re_password}
onChange={(e) => setRe_password(e.target.value)}
/>
</Form.Group>
<Button type="submit" className="auth-button">Sign Up</Button>
</Form>
<Row className="p-2">
<Col>
<div className=""> Already have an account? <Link to="/login">Login</Link></div>
</Col>
<Col>
</Col>
</Row>
</Container>
)
}
export default connect(null, {signup}) (RegisterScreen)
Redux store
import thunk from 'redux-thunk'
import { composeWithDevTools } from 'redux-devtools-extension'
const middleware = [thunk]
const store = createStore(
reducer,
composeWithDevTools(applyMiddleware(...middleware))
)
export default store
I have spent several hours still can't find where the error is coming from in the code. How do I fix it?
You seem to be mixing two different APIs for Redux: the hooks API and the connect API.
When using function components, you don't need connect at all, hooks are enough, so try to remove the last line with connect (export RegisterScreen directly instead), and remove the signup prop (use the imported signup action creator directly).
Related
I am trying to use a container to simplify some styling while learning React and I am having some issues.
Currently this is my Component
import React from 'react'
import { Container, Row, Col } from 'react-bootstrap'
const FormContainer = ({ childern }) => {
return (
<Container>
<Row className="justify-content-md-center">
<Col xs={12} md={6}>
{childern}
</Col>
</Row>
</Container>
)
}
export default FormContainer
And this is where I am loading it into
import React, { useState, useEffect } from 'react'
import { Link } from 'react-router-dom'
import { Form, Button, Row, Col } from 'react-bootstrap'
import { useDispatch, useSelector } from 'react-redux'
import Message from '../components/Message'
import Loader from '../components/Loader'
import FormContainer from '../components/FormContainer'
import { login } from '../actions/userActions'
const LoginScreen = ({location, history}) => {
const [email, setEmail] = useState('')
const [password, setPassword] = useState('')
const dispatch = useDispatch()
const userLogin = useSelector(state => state.userLogin)
const {loading, error, userInfo} = userLogin
const redirect = location.search ? location.search.split('=')[1] : '/' // Takes location of Url, splits after the equals sign, and everything right of it or takes us back to '/'
useEffect(()=> {
if(userInfo) {
history.push(redirect)
}
}, [history, userInfo, redirect])
const submitHandler = (e) => {
e.preventDefault()
dispatch(login(email, password))
}
return (
<FormContainer>
<h1>Sign In</h1>
{error && <Message variant='danger'>{error}</Message>}
{loading && <Loader/>}
<Form onSubmit={submitHandler}>
<Form.Group controlId='email'>
<Form.Label>Email Address</Form.Label>
<Form.Control type='email' placeholder="Enter email" value={email} onChange={(e) => setEmail(e.target.value)}>
</Form.Control>
</Form.Group>
<Form.Group controlId='password'>
<Form.Label>Password</Form.Label>
<Form.Control type='password' placeholder="Enter password" value={password} onChange={(e) => setPassword(e.target.value)}>
</Form.Control>
</Form.Group>
<Button type='submit' variant='primary'>
Sign In
</Button>
</Form>
<Row className='py-3'>
<Col>
New Customer <Link to={redirect ? `/register?redirect=${redirect}` : '/register'}>
Register
</Link>
</Col>
</Row>
</FormContainer>
)
}
export default LoginScreen
When I remove FormContainer and replace it with a div the LoginScreen info shows up but without the styling. Otherwise it is blank with no issues in the Console. My other components seem to to show some stuff, but not the text.
Any help would be amazing.
Thanks!
I am utilizing react bootstrap and firebase to create a Modal to allow signIn - I have a create user component fully implemented but when I try to use a modal to handle my sign in it error out . Any help is much appreciated.
Uncaught (in promise) TypeError: Cannot destructure property 'email' of 'event.target.elements' as it is undefined.
import React, { useState, useCallback, useContext } from 'react';
import './Homepage.css';
import { Button, Modal, Form } from 'react-bootstrap';
import { Link, Redirect } from 'react-router-dom';
// Firebase Credentials
import app from '../firebase'
import { AuthContext } from '../Auth'
const Homepage = ({ history }) => {
const [show, setShow] = useState(false);
const handleClose = () => setShow(false);
const handleShow = () => setShow(true);
const handleLogin = useCallback(
async (event)=> {
event.preventDefault();
const { email, password } = event.target.elements;
try {
await app.auth().signInWithEmailAndPassword(email.value, password.value);
history.push('/generate')
}catch(error){
alert(error);
}
},[history]
);
const { currentUser } = useContext(AuthContext);
if (currentUser) {
return
}
return (
<div className='welcomeContainer'>
<h1 className='welcomeBanner'>Resu.Me</h1>
<h4 className='welcomeMessage'>
Resu.me is the easiest solution for cover letters and resumes.
<br></br> Simply input your credentials and contact information - We
handle the rest!
</h4>
<Link className='genLeadButton' to='/generate'>
<Button>Let's make a Resume</Button>
</Link>
<Button onClick={handleShow} className='loginButton'>
Log In to Resu.me
</Button>
{/* <h2 className="servicesBanner">Score the job you deserve with the help of our tools</h2> */}
<h2 className='servicesContainer serviceA'>PLACEHOLDER A</h2>
<h2 className='servicesContainer serviceB'>PLACEHOLDER B</h2>
<h2 className='servicesContainer serviceC'>PLACEHOLDER C</h2>
<Modal show={show} onHide={handleClose}>
<Modal.Header closeButton>
<Modal.Title>Login</Modal.Title>
</Modal.Header>
<Modal.Body>
<Form>
<Form.Group controlId='formBasicEmail'>
<Form.Label className='label'>Email Address</Form.Label>
<Form.Control
name='email'
type='email'
placeholder="Login email"
/>
</Form.Group>
<br></br>
<Form.Group controlId='formBasicPassword'>
<Form.Label>Password</Form.Label>
<Form.Control
name='password'
type='password'
placeholder='Password'
/>
</Form.Group>
</Form>
</Modal.Body>
<Modal.Footer>
<Button variant='secondary' type='submit' onClick={handleLogin}>
Login
</Button>
</Modal.Footer>
</Modal>
</div>
);
};
export default Homepage;
const { email, password } = event.target.elements;
will map the value by the input id (controlId for react-bootstrap forms) in these two rows
<Form.Group controlId='formBasicEmail'>
<Form.Group controlId='formBasicPassword'>
so you have to make them identical like:
const { formBasicEmail, formBasicPassword } = event.target.elements;
or
<Form.Group controlId='email'>
<Form.Group controlId='password'>
I'm starting to learn React and following the tutorial and got stuck with this error when I was trying to upload picture. When I press upload button, this error "Objects are not valid as a React child (found: object with keys {username}). If you meant to render a collection of children, use an array instead." shown up and I couldn't reload the page anymore.
Render Error
Here are the codes:
1.App.js
import React, { useEffect, useState } from "react";
import './App.css';
import Post from './Post';
import { auth, db } from "./firebase";
import Modal from '#material-ui/core/Modal';
import { makeStyles } from '#material-ui/core/styles';
import { Button, Input } from "#material-ui/core";
import ImageUpload from './ImageUpload';
function getModalStyle() {
const top = 50;
const left = 50;
return {
top: `${top}%`,
left: `${left}%`,
transform: `translate(-${top}%, -${left}%)`,
};
}
const useStyles = makeStyles((theme) => ({
paper: {
position: 'absolute',
width: 400,
backgroundColor: theme.palette.background.paper,
border: '2px solid #000',
boxShadow: theme.shadows[5],
padding: theme.spacing(2, 4, 3),
},
}));
function App() {
const classes = useStyles();
const [modalStyle] = React.useState(getModalStyle);
const [posts, setPosts] = useState([]);
const [open, setOpen] = useState(false);
const [openSignIn, setOpenSignIn] = useState('');
const [username, setUsername] = useState('');
const [email, setEmail] = useState('');
const [password, setPassword] = useState('');
const [user, setUser] = useState(null);
//UseEffect -> Run a piece of code based on a specific condition
useEffect(() => {
const unsubscribe = auth.onAuthStateChanged((authUser) => {
if (authUser) {
//user has logged in...
console.log(authUser);
setUser(authUser);
} else {
//user has logged out...
setUser(null);
}
return () => {
//perform some cleanup action
unsubscribe();
}
})
}, [user, username]);
useEffect(() => {
//this is where the code runs
db.collection('posts').onSnapshot(snapshot => {
//everytime a new post is added, this code fires...
setPosts(snapshot.docs.map(doc => ({
id: doc.id,
post: doc.data()
})));
})
}, []);
const signUp = (event) => {
event.preventDefault();
auth
.createUserWithEmailAndPassword(email, password)
.then((authUser) => {
return authUser.user.updateProfile({
displayName: username
})
})
.catch((error) => alert(error.message))
}
const signIn = (event) => {
event.preventDefault();
auth
.signInWithEmailAndPassword(email, password)
.catch((error) => alert(error.message))
setOpenSignIn(false);
}
return (
<div className="app">
{user?.displayName ? (
<ImageUpload username={user.displayName} />
) : (
<h3>Sorry you need to login to upload</h3>
)}
<Modal
open={open}
onClose={() => setOpen(false)}
>
<div style={modalStyle} className={classes.paper}>
<form className="app__signup">
<center>
<img
className="app__headerImage"
src="https://www.instagram.com/static/images/web/mobile_nav_type_logo.png/735145cfe0a4.png"
alt=""
/>
</center>
<Input
placeholder="username"
type="text"
value={username}
onChange={(e) => setUsername(e.target.value)}
/>
<Input
placeholder="email"
type="text"
value={email}
onChange={(e) => setEmail(e.target.value)}
/>
<Input
placeholder="password"
type="password"
value={password}
onChange={(e) => setPassword(e.target.value)}
/>
<Button type="submit" onClick={signUp}>Sign Up</Button>
</form>
</div>
</Modal>
<Modal
open={openSignIn}
onClose={() => setOpenSignIn(false)}
>
<div style={modalStyle} className={classes.paper}>
<form className="app__signup">
<center>
<img
className="app__headerImage"
src="https://www.instagram.com/static/images/web/mobile_nav_type_logo.png/735145cfe0a4.png"
alt=""
/>
</center>
<Input
placeholder="email"
type="text"
value={email}
onChange={(e) => setEmail(e.target.value)}
/>
<Input
placeholder="password"
type="password"
value={password}
onChange={(e) => setPassword(e.target.value)}
/>
<Button type="submit" onClick={signIn}>Sign In</Button>
</form>
</div>
</Modal>
<div className="app__header">
<img
className="app__headerImage"
src="https://www.instagram.com/static/images/web/mobile_nav_type_logo.png/735145cfe0a4.png"
alt="" />
</div>
{user ? (
<Button onClick={() => auth.signOut()}>Logout</Button>
) : (
<div className="app__loginContainer">
{/* : is stand for OR */}
<Button onClick={() => setOpenSignIn(true)}>Sign In</Button>
<Button onClick={() => setOpen(true)}>Sign Up</Button>
</div>
)}
<h1>Hello Joes! Let's build an Instagram CLone with React</h1>
{
posts.map(({ id, post }) => (
<Post key={id} username={post.username} caption={post.caption} imgUrl={post.imgUrl} />
))
}
</div>
);
}
export default App;
Here is the ImageUpload file
2. ImageUpload.js
import { Button } from '#material-ui/core'
import React, { useState } from 'react';
import { storage, db } from './firebase';
import firebase from "firebase";
function ImageUpload(username) {
const [image, setImage] = useState(null);
const [progress, setProgress] = useState(0);
const [caption, setCaption] = useState('');
const handleChange = (e) => {
if (e.target.files[0]) {
setImage(e.target.files[0]);
}
};
const handleUpload = () => {
const uploadTask = storage.ref(`images/${image.name}`).put(image);
uploadTask.on(
"state_change",
(snapshot) => {
//progress funtion...
const progress = Math.round(
(snapshot.bytesTransferred / snapshot.totalBytes) * 100
);
setProgress(progress);
},
(error) => {
//Error function...
console.log(error);
alert(error.message);
},
() => {
// Complete function...
storage
.ref("images")
.child(image.name)
.getDownloadURL()
.then(url => {
// Post image on db
db.collection("posts").add({
timestamp: firebase.firestore.FieldValue.serverTimestamp(),
caption: caption,
imageUrl: url,
username: username
});
setProgress(0);
setCaption("");
setImage(null);
});
}
);
};
return (
<div>
{/* I want to have... */}
{/* Caption input */}
{/* File picker */}
{/* Post button */}
<progress value={progress} max="100" />
<input type="text" placeholder="Enter a caption..." onChange={event => setCaption(event.target.value)} value={caption} />
<input type="file" onChange={handleChange} />
<Button onClick={handleUpload}>
Upload
</Button>
</div>
)
}
export default ImageUpload
Thanks guys!
This line is wrong:
<progress value={progress} max="100" />
React components have to be upper case.
Your progress is a number, not a React component
You probably wanted to import the component Progress and write:
<Progress value={progress} max="100" />
I am using the context API to implement authentication, but I cannot figure out where my problem, i'm getting error when trying to render Register component, here is my code:
Register.js
import React, { Component, useState, useEffect } from 'react';
import { AvForm, AvGroup, AvInput, AvFeedback } from 'availity-reactstrap-validation';
import { AuthConsumer, useAuth } from '../AuthContext';
const Register = (props) => {
const [user, setUser] = useState({ email: '', password: ''})
const handleChange = (e) => setUser({ ...user, [e.target.name]: e.target.value })
return (
<React.Fragment>
<Container>
<AuthConsumer>
{ ({ isAuth, signup }) => (
<AvForm onSubmit={signup.bind(this, user, props)} onValidSubmit={this.handleValidSubmit} className="authentication-form">
<AvGroup className="">
<Label for="email">Email</Label>
<InputGroup>
<AvInput onChange={handleChange} type="email" value={user.email} placeholder="email" name="email"/>
</InputGroup>
</AvGroup>
<AvGroup className="mb-3">
<Label for="password">Password</Label>
<InputGroup>
<AvInput onChange={handleChange} type="password"value={user.password} placeholder="password" name="password"/>
</InputGroup>
</AvGroup>
</AvForm>
)}
</AuthConsumer>
</Container>
</React.Fragment>
)
}
export default Register
AuthContext.js
There is a code sample of authcontext that i used: Codesandbox
Error log:
Uncaught TypeError: Cannot read property 'isAuth' of undefined
at Register.js:39
at updateContextConsumer (react-dom.development.js:18206)
at beginWork (react-dom.development.js:18556)
Register.js:39
<AuthConsumer>
>> { ({ isAuth, signup }) => (
<AvForm onSubmit={signup.bind(this, user, props)}
onValidSubmit={this.handleValidSubmit} className="authentication-form">
I'm not great at testing, and new to Jest and Enzyme. I have a Login component that consists of two TextInput components for username and password and a Button component. I am testing each component individually.
I would just like to test that a username and password was returned by onLogin.
Here is the component:
export const onLogin = (user, password) => {
console.log('User', user, 'Password', password)
return [user, password];
};
function Login() {
const [user, setUser] = useState("");
const [password, setPassword] = useState("");
return (
<LoginWrapper>
<Branding brand={brand.brandName} />
<FormWrapper onSubmit={(e) => { e.preventDefault(); onLogin(user, password) }}>
<Stack>
<TextInput
className="username"
type="text"
label="Username"
onChange={e => setUser(e.target.value)}
/>
</Stack>
<Stack>
<TextInput
className="password"
type="password"
label="Password"
onChange={e => {setPassword(e.target.value); console.log('user', user)}}
/>
</Stack>
<Stack padding="0" align="right">
<Button type="submit">Login</Button>
</Stack>
</FormWrapper>
</LoginWrapper>
);
}
export default Login;
My test:
describe("<Login />", () => {
it("renders text input correctly", () => {
const tree = renderer.create(<ThemeProvider theme={themes.default}><Login /></ThemeProvider>).toJSON();
expect(tree).toMatchSnapshot();
});
it("calls onLogin when button clicked", () => {
const onSubmitMock = jest.fn();
const component = Enzyme.mount(
<ThemeProvider theme={themes.default}><Login onSubmit={onSubmitMock} /></ThemeProvider>
);
component.find("input.username").simulate('change', { target: { value: 'myUser' } })
component.find("input.password").simulate('change', { target: { value: 'myPassword' } })
component.find("form").simulate("submit");
console.log("onClickMock.mock", onSubmitMock.mock)
expect(onSubmitMock).toBeCalled()
});
});
Results:
Expected mock function to have been called, but it was not called.
Your testing approach is correct except for:
In your test, you are mocking a callback function and passing it as a property onSubmit to your component. Then you need to call this function from your component when you submit the form.
Instead, you are calling the onLogin function on your component that does not have any
repercussion.
In order to fix this, declare the properties on your component function as a parameter, and call props.onSubmit on your form submit.
function Login(props) {
const [user, setUser] = useState("");
const [password, setPassword] = useState("");
return (
<LoginWrapper>
<Branding brand={brand.brandName} />
<FormWrapper onSubmit={(e) => { e.preventDefault(); props.onSubmit(user, password) }}>
<Stack>
<TextInput
className="username"
type="text"
label="Username"
onChange={(e) => setUser(e.target.value)}
/>
</Stack>
<Stack>
<TextInput
className="password"
type="password"
label="Password"
onChange={(e) => { setPassword(e.target.value) }}
/>
</Stack>
<Stack padding="0" align="right">
<Button type="submit">Login</Button>
</Stack>
</FormWrapper>
</LoginWrapper>
);
}