I have a blog webapp that I'm developing.
The theme is a car mechanic's workshop called "Bob's Garage".
I have a "blog" page, then the rest of the pages are basically the same as the "blog" page but with different content (e.g "User Feedback page". "User reviews page", etc..
The "blog" page is working, but the "feedback" and "review" page are not.
The code is virtually identical between the pages, just some different names (e.g "AddBlog()" becomes "AddFeedback()")
Here is the code:
AddFeedback.js:
//rafcp
// import fragment, useState
import React, {Fragment, useState} from 'react';
import PropTypes from 'prop-types';
// import useNavigate
import {useNavigate} from 'react-router-dom';
// import connect
import { connect } from 'react-redux';
// import our addFeedback function.
import { addFeedback} from '../../actions/feedbackActions';
const AddFeedback = ({ addFeedback, userId }) => {
// Create component state.
const [formData, setFormData] = useState({
Author: '',
Heading:'',
Feedback:'',
Location:'',
errors: {
body: 'You must enter your feedback in the box provided'
}
});
console.log("formData.body = " + formData.body); // formData.body is the user input in the textbox of the Add New Blog Entry page
// Use destructuring to pull the variables out of our state.
const { Author, Heading, Feedback, Location, errors } = formData;
// Create our navigate variable.
const navigate = useNavigate();
// on Change function.
const onChange = e => {
setFormData({
...formData, [e.target.name]: e.target.value
});
}
// Create our on submit
const onSubmit = async(e) => {
e.preventDefault();
// Test that the onSubmit is called.
console.log('Onsumbit - Add running...');
// Check for errors / validation.
if( Feedback === '' || Feedback === undefined){
console.log('feedback empty');
// Save an error message to the state using the errors object.
// remember that we also need to include everything that is in the state.
setFormData({
...formData,
errors:{
body: 'You must enter feedback in the box above',
Heading: 'You must enter your heading in the box above',
Author: 'You must enter your name in the box above',
Feedback: 'You must enter your feedback in the box above',
Location: 'You must enter your location in the box above'
}
});
// stop the onSubmit running.
return;
} else {
setFormData({
...formData,
errors:{
Author: '',
Heading: '',
Feedback: '',
Location: '',
body: ''
}
});
}
let publishDate = new Date();
let publishDay = publishDate.getDate();
let publishMonth = publishDate.getMonth() + 1;
let publishYear = publishDate.getFullYear();
console.log("AddFeedback.js: userId = " + userId);
// create a newItem to add to our feedback list.
const newItem = {
Author: Author,
Heading: Heading,
Feedback: Feedback,
PublishDate: publishYear + "-" + publishMonth + "-" + publishDay,
Location: Location,
Id: userId,
UserUserId: userId
//UserUserId: userUserId
//UserUserId:auth.user.userId
}
console.log(newItem);
// Send our newItem to an API or state managemeht system.
// Call our addFeedback function.
addFeedback(newItem);
// We can do other things after this, like redirect the browser.
navigate('/feedback');
}; // end of onSubmit.
return (
<Fragment>
<h2 className='text-primary'>Add New Feedback</h2>
<div className='card mb-3'>
<div className='card-header'>
Add Your Feedback
</div>
<div className='card-body'>
<form onSubmit={e => onSubmit(e)}>
<div className='mb-3'>
<label htmlFor='body'>Heading:</label>
<textarea
className={`form-control ${errors.heading ? "is-invalid" : 'is-valid'}`}
id='heading'
placeholder='Your Heading'
name='Heading'
value={Heading}
// Add in our onChange event
onChange={ e => onChange(e)}
></textarea>
{errors.heading && <div className='invalid-feedback'>
{errors.heading}
</div>}
</div>
<div className='mb-3'>
<label htmlFor='body'>Author:</label>
<textarea
className={`form-control ${errors.heading ? "is-invalid" : 'is-valid'}`}
id='author'
placeholder='Author'
name='Author'
value={Author}
// Add in our onChange event
onChange={ e => onChange(e)}
></textarea>
{errors.heading && <div className='invalid-feedback'>
{errors.heading}
</div>}
</div>
<div className='mb-3'>
<label htmlFor='body'>Location:</label>
<textarea
className={`form-control ${errors.heading ? "is-invalid" : 'is-valid'}`}
id='location'
placeholder='Location'
name='Location'
value={Location}
// Add in our onChange event
onChange={ e => onChange(e)}
></textarea>
{errors.heading && <div className='invalid-feedback'>
{errors.heading}
</div>}
</div>
<div className='mb-3'>
<label htmlFor='body'>Have your say below:</label>
<textarea
className={`form-control ${errors.body ? "is-invalid" : 'is-valid'}`}
id='feedback'
placeholder='Your Feedback'
name='Feedback'
value={Feedback}
// Add in our onChange event
onChange={ e => onChange(e)}
></textarea>
{errors.body && <div className='invalid-feedback'>
{errors.body}
</div>}
</div>
{/* <div className='mb-3'>
// <label htmlFor='name'>Name</label>
// <input
// type='text'
// className='form-control'
// id='name'
// placeholder='Name'
// name='name'
// value={name}
// onChange={e => onChange(e)}
// />
</div> */}
{/* <div className='mb-3'>
<label htmlFor='image'>Image</label>
<input
type='text'
className='form-control'
id='image'
placeholder='URL for image'
name='image'
value={image}
onChange={e => onChange(e)}
/>
</div> */}
<div className='d-grid gap-2'>
<input type='submit' value='Add Feedback' className='btn btn-light '/>
</div>
</form>
</div>
</div>
</Fragment>
)
}
// Proptypes
AddFeedback.propTypes = {
addFeedback: PropTypes.func.isRequired
}
//mapState to props
//user from state(auth)
//get the user id of the person logged in
//auth.user
//auth.user.userId
//pass that userId through in onSubmit function
const mapStateToProps = state => ({
userId: state.auth.user.userId
})
// add in connect.
// We do not need anything from the state or store.
// that means null, as we do not need mapState to props.
export default connect(mapStateToProps, {addFeedback})(AddFeedback)
AddBlog.js:
//rafcp
// import fragment, useState
import React, {Fragment, useState} from 'react';
import PropTypes from 'prop-types';
// import useNavigate
import {useNavigate} from 'react-router-dom';
// import connect
import { connect } from 'react-redux';
// import our addFeedback function.
import { addBlog} from '../../actions/blogActions';
const AddBlog = ({ addBlog, userId }) => {
// Create component state.
const [formData, setFormData] = useState({
//Post: '',
//Author: '',
Heading: '',
errors: {
Post: 'You must enter your blog post in the box provided'
}
});
console.log("formData.Post = " + formData.Post); // formData.Post is the user input in the textbox of the Add New Blog Entry page
// Use destructuring to pull the variables out of our state.
const { Post, Author, ImageURL, errors, Heading } = formData;
// Create our navigate variable.
const navigate = useNavigate();
// on Change function.
const onChange = e => {
setFormData({
...formData, [e.target.name]: e.target.value
});
}
// Create our on submit
const onSubmit = async(e) => {
e.preventDefault();
// Test that the onSubmit is called.
console.log('Onsumbit - Add running...');
// Check for errors / validation.
if( Post === '' || Post === undefined){
console.log('post empty');
// Save an error message to the state using the errors object.
// remember that we also need to include everything that is in the state.
setFormData({
...formData,
errors:{
Post: 'You must enter blog entry in the box above'
}
});
// stop the onSubmit running.
return;
} else {
setFormData({
...formData,
errors:{
Post: ''
}
});
}
let publishDate = new Date();
let publishDay = publishDate.getDate();
let publishMonth = publishDate.getMonth() + 1;
let publishYear = publishDate.getFullYear();
console.log("AddBlog.js: userId = " + userId);
// create a newItem to add to our feedback list.
const newItem = {
Post: Post,
Author: Author,
Heading: Heading,
ImageURL: ImageURL,
PublishDate: publishYear + "-" + publishMonth + "-" + publishDay, // create function
UserUserId: userId
//UserUserId: userUserId
//UserUserId:auth.user.userId
}
console.log(newItem);
// Send our newItem to an API or state managemeht system.
// Call our addFeedback function.
addBlog(newItem);
// We can do other things after this, like redirect the browser.
navigate('/blog');
}; // end of onSubmit.
return (
<Fragment>
<h2 className='text-primary'>Add New Blog Entry</h2>
<div className='card mb-3'>
<div className='card-header'>
Add Your Blog Entry
</div>
<div className='card-body'>
<form onSubmit={e => onSubmit(e)}>
<div className='mb-3'>
{/* Heading */}
<label htmlFor='body'>Heading:</label>
<textarea
className={`form-control ${errors.heading ? "is-invalid" : 'is-valid'}`}
id='heading'
placeholder='Your Heading'
name='Heading'
value={Heading}
// Add in our onChange event
onChange={ e => onChange(e)}
></textarea>
{errors.heading && <div className='invalid-feedback'>
{errors.heading}
</div>}
</div>
{/* Image URL */}
<div className='mb-3'>
<label htmlFor='body'>Image URL:</label>
<textarea
className={`form-control ${errors.ImageURL ? "is-invalid" : 'is-valid'}`}
id='imageurl'
placeholder='Your image URL'
name='ImageURL'
value={ImageURL}
// Add in our onChange event
onChange={ e => onChange(e)}
></textarea>
{errors.ImageURL && <div className='invalid-feedback'>
{errors.ImageURL}
</div>}
</div>
{/* Author */}
<div className='mb-3'>
<label htmlFor='body'>Author:</label>
<textarea
className={`form-control ${errors.Author ? "is-invalid" : 'is-valid'}`}
id='author'
placeholder='Your Heading'
name='Author'
value={Author}
// Add in our onChange event
onChange={ e => onChange(e)}
></textarea>
{errors.heading && <div className='invalid-feedback'>
{errors.heading}
</div>}
</div>
{/* Author */}
<div className='mb-3'>
<label htmlFor='body'>Have your say below:</label>
<textarea
className={`form-control ${errors.Post ? "is-invalid" : 'is-valid'}`}
id='post'
placeholder='Your Blog Post'
name='Post'
value={Post}
// Add in our onChange event
onChange={ e => onChange(e)}
></textarea>
{errors.Post && <div className='invalid-feedback'>
{errors.Post}
</div>}
</div>
{/* <div className='mb-3'>
// <label htmlFor='name'>Name</label>
// <input
// type='text'
// className='form-control'
// id='name'
// placeholder='Name'
// name='name'
// value={name}
// onChange={e => onChange(e)}
// />
</div> */}
{/* <div className='mb-3'>
<label htmlFor='image'>Image</label>
<input
type='text'
className='form-control'
id='image'
placeholder='URL for image'
name='image'
value={image}
onChange={e => onChange(e)}
/>
</div> */}
<div className='d-grid gap-2'>
<input type='submit' value='Add Blog Entry' className='btn btn-light '/>
</div>
</form>
</div>
</div>
</Fragment>
)
}
// Proptypes
AddBlog.propTypes = {
addBlog: PropTypes.func.isRequired
}
//mapState to props
//user from state(auth)
//get the user id of the person logged in
//auth.user
//auth.user.userId
//pass that userId through in onSubmit function
const mapStateToProps = state => ({
userId: state.auth.user.userId
})
// add in connect.
// We do not need anything from the state or store.
// that means null, as we do not need mapState to props.
export default connect(mapStateToProps, {addBlog})(AddBlog)
I cannot understand why 'userId' is evaluating to 'null'...
Do any of you have any ideas as to why this error might be occurring?
Here is a screenshot of the Redux Dev Tools 'State' windows whilst on the 'AddFeedback.js' page.
Please let me know if you need me to post up any other source files, such as Context.js or similar.
Kind Regards,
John Melbourne, Australia
I found the solution to my problem.
As a React newbie I had inadvertently used an < a > tag to navigate to the AddFeedback.js page, which caused state to be lost, which is why userId became null.
So, to all you React newbies out there:
Use a React < Link to=/your/path/here > Your link text < / Link>
Kind Regards, John Melbourne, Australia