Forms with Redux - reactjs

I don't wanna use redux-form, i would like to dispatch all values of the form in the container place.
The question is how to get title and content (together) by handleChange function
The container file looks like this:
import { connect } from "react-redux";
import AddPost from "../components/Posts/AddPost";
import { updateNewPost, clearNewPost } from "../actions/new-post";
import { createPost } from "../actions/posts";
const mapStateToProps = ({ title, content, auth }) => {
return { title, content, auth };
};
const mapDispatchToProps = dispatch => {
return {
handleChange(event) {
dispatch(updateNewPost(event.target.value));
},
handleSubmit(event, title, content, uid) {
event.preventDefault();
dispatch(createPost({ title, content, uid }));
dispatch(clearNewPost());
}
};
};
export default connect(mapStateToProps, mapDispatchToProps)(AddPost);
The Component file looks like this:
import React, { PropTypes } from "react";
const AddPost = ({ title, content, auth, handleChange, handleSubmit }) => (
<form onSubmit={event => handleSubmit(event, title, content, auth.uid)}>
<input
type="text"
placeholder="title"
value={title}
onChange={handleChange}
/>
<input
type="text"
placeholder="content"
value={content}
onChange={handleChange}
/>
<input type="submit" value="Post" />
</form>
);
AddPost.propTypes = {
title: PropTypes.string,
content: PropTypes.string,
auth: PropTypes.object,
handleChange: PropTypes.func,
handleSubmit: PropTypes.func
};
export default AddPost;
thank you for help

You got the redux part wrong.
What you want to do is setup a key in your store for each value and update it accordingly.
// store
const store = {
title: "",
content: ""
}
// reducer
const reducer = (state, action) => {
switch(action.type) {
case "UPDATE":
return Object.assign({}, state, {[action.field]: action.payload});
default:
return state;
}
}
// action creator
const updateField = (value, field) {
return {
type: "UPDATE",
payload: value
field
};
}
// form input
<input
type="text"
placeholder="title"
value={title}
onChange={(e) => updateField(e.taget.value, "title")}
/>
<input
type="text"
placeholder="content"
value={content}
onChange={(e) => updateField(e.taget.value, "content")}
/>
Another option is to convert your stateless function into a class and manage the inputs state locally, then once you'v validated that your data is good you can dispatch a single action that will contain everything.
// form component
class Form extends React.Component {
constructor() {
super();
this.state = {title: "", content: ""};
this.validateAndSubmit = this.validateAndSubmit.bind(this);
}
validateAndSubmit() {
const {title, content} = this.state;
if (!title.length || !content.length)
return;
this.props.submitFormData({title, content});
}
render() {
return (
<div>
<input
type="text"
placeholder="title"
value={this.state.title}
onChange={(e) => this.setState({title: e.target.value}, this.validateAndSubmit)}/>
<input
type="text"
placeholder="content"
value={content}
onChange={(e) => this.setState({content: e.target.value}, this.validateAndSubmit)}/>
</div>
);
}
}
// action creator
const submitFormData = (data) => {
return {
type: "COMPLETE",
payload: data
};
}
// reducer
const reducer = (state, action) => {
switch(action.type) {
case "COMPLETE":
// you have your form data here...
default:
return state;
}
}
This code is not tested but I hope you get the idea.
Good Luck :)

Related

How to reset text field React Formik?

I have a container component that works with Redux, in it I determined the state of the text field and throw it into the form component that works with Formik
But I have such a problem that after clicking the button, the text field is not cleared
How do I fix this ? I've seen a lot about resetForm() from Formik, but I can't work with it
TaskInput.jsx
export const TaskInput = ({ value, onChange, onAdd }) => {
const formik = useFormik({
initialValues: {
text: value,
},
})
return (
<Container>
<Formik>
<FormWrapper onSubmit={onAdd}>
<Input
id="task"
type="text"
placeholder="Add a task"
value={formik.text}
onChange={onChange}
/>
<AddButton type="submit">Add</AddButton>
</FormWrapper>
</Formik>
</Container>
)
}
Task.jsx(component container)
class Task extends PureComponent {
constructor(props) {
super(props)
this.state = {
taskText: '',
}
}
handleInputChange = e => {
this.setState({
taskText: e.target.value,
})
}
handleAddTask = () => {
const { taskText } = this.state
const { addTask } = this.props
addTask(uuidv4(), taskText, false)
this.setState({
taskText: '',
})
}
render() {
const { taskText } = this.state
return (
<>
<TaskInput
onAdd={this.handleAddTask}
onChange={this.handleInputChange}
value={taskText}
/>
...
</>
)
}
}
const mapStateToProps = ({ tasks }) => {
return {
tasks,
}
}
export default connect(mapStateToProps, {
addTask,
removeTask,
completeTask,
editTask,
})(Task)
Solution
export const TaskInput = ({ value, onChange, onAdd }) => {
const inputRef = useRef(null)
const formik = useFormik({
initialValues: {
text: value,
},
})
const onSubmit = () => {
onAdd(),
inputRef.current.value = ''
}
return (
<Container>
<Formik>
<FormWrapper onSubmit={onSubmit}>
<Input
ref={inputRef}
id="task"
type="text"
placeholder="Add a task"
value={formik.text}
onChange={onChange}
/>
<AddButton type="submit">Add</AddButton>
</FormWrapper>
</Formik>
</Container>
)
}

react page is rendering blank [duplicate]

This question already exists:
return not displaying page data react functional component
Closed 1 year ago.
I have this, my entire react page:
import React, { useState, useEffect } from "react";
import axios from "axios";
import { useHistory } from "react-router-dom";
import { useMemo } from "react";
import { connect } from "react-redux";
import AdminNav from "../../../components/admin/AdminNav"
import AdminAboutUsNav from "../../../components/admin/AdminAboutUsNav"
import Header from "../../../components/app/Header";
import { setNavTabValue } from '../../../store/actions/navTab';
import { makeStyles, withStyles } from "#material-ui/core/styles";
import "../../../styles/AddMembershipPage.css";
const AddMembershipPage = (props) => {
const history = useHistory();
const [myData, setMyData] = useState({});
let ssoDetails = {
name: props.blue.preferredFirstName + " " + props.preferredLastName,
email: props.blue.preferredIdentity,
cnum: props.blue.uid,
empType: "part-time"
}
this.state = {
cnum: ssoDetails.cnum,
empType: ssoDetails.empType,
email: ssoDetails.email,
name: ssoDetails.name,
phone: "",
// building: building,
siteList: "",
status: ""
};
const handleInputChange = (e) => {
this.setState({
[e.target.name]: e.target.value,
});
};
const handleSubmit = (e) => {
e.preventDefault();
var date = Date().toLocaleString();
const { cnum, empType, email, name, phone, siteList, status } = this.state;
const selections = {
cnum: cnum,
empType: empType,
email: email,
name: name,
phone: phone,
// building: building,
siteList: siteList,
status: status
};
axios
.post("/newMembership", selections)
.then(
() => console.log("updating", selections),
(window.location = "/admin/services")
)
.catch(function (error) {
// alert(error)
window.location = "/admin/services/new";
});
};
const useStyles = makeStyles((theme) => ({
root: {
flexGrow: 1,
backgroundColor: theme.palette.background.paper,
},
}));
const classes = useStyles();
return (
<div className={classes.root}>
<AdminNav />
{/* <Header title="Services - Admin" /> */}
{/* <AdminAboutUsNav /> */}
<div className="App">
<form onSubmit={this.handleSubmit}>
<h1>Join Us!</h1>
<input value={ssoDetails.name} readOnly name="name" onChange={this.handleInputChange}></input>
<input type="email" value={ssoDetails.email} readOnly name="email" onChange={this.handleInputChange}></input>
<input type="hidden" value={ssoDetails.cnum} readOnly name="cnum" onChange={this.handleInputChange}></input>
<input type="text" value={ssoDetails.empType} readOnly name="empType" onChange={this.handleInputChange}></input>
<input type="text" placeholder="Phone Number" name="phone" onChange={this.handleInputChange}></input>
<input type="text" placeholder="Site List" name="siteList" onChange={this.handleInputChange}></input>
{/* <input type="password" placeholder="Password"></input> */}
<button type="submit">Register</button>
</form>
</div>
</div>
);
}
const mapStateToProps = (state) => {
return {
siteTab: state.siteTab,
blue: state.blue
}
}
const mapDispatchToProps = (dispatch, props) => ({
setNavTabValue: (value) => dispatch(setNavTabValue(value))
});
export default connect(mapStateToProps, mapDispatchToProps)(AddMembershipPage);
however, when I try to run this page, it just shows up blank. It started doing this after I added const handleInputChange, and const handleSubmit to the code. I am basically just trying to submit a form, and it is more complex then I imagined. Before I added those 2 things I just mentioned, the page was working perfectly. but now, I cannot figure it out, and really could use some guidance/help to try to fix this up. any ideas?
It's function component so you don't need to call with this.handleSubmit
Just change it to the onSubmit={handleSubmit}> and onChange={handleInputChange}>
Also remove this.state and use useState instead because this.state was available in class based component not in the function component.

When I submit the contact form its shows error in react js

When I submit the contact form its shows error like error sending message.
Please help me is there any mistake in my code. I am new in react js.
This is the code i have used. please help me is there any logical or syntax mistake in this.
import React, { FormEvent, ReactElement } from 'react';
import { noop } from 'rxjs';
import { EmailData } from '../../../models/i-email-data';
import { EmailInput, MessageInput, NameInput, SubjectInput } from './';
interface Props {
onSubmit: (data: EmailData) => Promise<any>;
}
*type* State = EmailData;
export class ContactForm extends React.Component<Props, State> {
public state: State = {
name: '',
email: '',
subject: '',
message: '',
};
public render(): ReactElement {
const { name, email, subject, message } = this.state;
return (
<>
<h3 className='contact-form__title'>Send a Message</h3>
<form className='form' onSubmit={this.onSubmit}>
<NameInput onChange={this.handleInputChange} value={name} />
<EmailInput onChange={this.handleInputChange} value={email} />
<SubjectInput onChange={this.handleInputChange} value={subject} />
<MessageInput onChange={this.handleInputChange} value={message} />
<div className='contact-form__btn-wrapper'>
<button className='contact-form__submit'> Submit</button>
</div>
</form>
</>
);
import React, { FormEvent, ReactElement } from 'react';
import { noop } from 'rxjs';
import { EmailData } from '../../../models/i-email-data';
import { EmailInput, MessageInput, NameInput, SubjectInput } from './';
interface Props {
onSubmit: (data: EmailData) => Promise<any>;
}
type State = EmailData;
export class ContactForm extends React.Component<Props, State> {
public state: State = {
name: '',
email: '',
subject: '',
message: '',
};
public render(): ReactElement {
const { name, email, subject, message } = this.state;
return (
<>
<h3 className='contact-form__title'>Send a Message</h3>
<form className='form' onSubmit={this.props.onSubmit}>
<NameInput onChange={this.handleInputChange} value={name} />
<EmailInput onChange={this.handleInputChange} value={email} />
<SubjectInput onChange={this.handleInputChange} value={subject} />
<MessageInput onChange={this.handleInputChange} value={message} />
<div className='contact-form__btn-wrapper'>
<button className='contact-form__submit'> Submit</button>
</div>
</form>
</>
);
}
private handleInputChange = (event: React.ChangeEvent<HTMLInputElement | HTMLTextAreaElement>): void => {
const { value, name } = event.target;
this.setState({ [name]: value } as any);
};
private onSubmit = async (e: FormEvent): Promise<void> => {
try {
e.preventDefault();
await this.props.onSubmit(this.state);
this.setState({ name: '', email: '', subject: '', message: '' });
} catch (e) {
noop();
}
};
This is the code i have used. please help me is there any logical or syntax mistake in this.
Based on your Props interface I guess you're passing an onSubmit callback as a prop.
If that's the case you need to extract onSubmit from this.props, so the following doesn't fail.
<form className='form' onSubmit={this.onSubmit}>
Edit 1: You need to pass your state in order to pass your form's state to the parent component
Change it to:
<form
className='form'
onSubmit={(event) => {
event.preventDefault();
this.props.onSubmit(this.state);
}}
>
Edit 2: I see you're missing handleInputChange as well.
A quick implementation of handleInputChange would look like this:
handleInputChange = (name) => ({ target }) => {
this.setState((prevState) => ({
...prevState,
[name]: target.value
});
}
And you could call it like so:
<NameInput onChange={this.handleInputChange('name')} value={name} />
<EmailInput onChange={this.handleInputChange('email')} value={email} />
there are a few issues with your code.
firstly you have no function called onSubmit so when you submit the form it's going to error coz it doesn't know this function.
<form className='form' onSubmit={this.onSubmit}>
you need to create a function like this:
onSubmit = () => {
// do stuff in here
}
there is also no function called: handleInputChange so you need to create this too, like so:
handleInputChange = () => {
// do stuff in here
}

Reactjs: Using same form for add and update

I crafted a reactjs crud app with help of a tutorial and it works great now. Now i am trying to merge two form together so that same form should be used for both add and update operation.
This is my allpost.js file
import React, { Component } from 'react';
import { connect } from 'react-redux';
import Post from '../components/Post';
import EditComponent from '../components/editComponent';
class AllPost extends Component {
render() {
return (
<div>
<h1>All Posts</h1>
{this.props.posts.map((post) => (
<div key={post.id}>
{post.editing ? <EditComponent post={post} key={post.id} /> :
<Post key={post.id} post={post} />}
</div>
))}
</div>
);
}
}
const mapStateToProps = (state) => {
return {
posts: state
}
}
export default connect(mapStateToProps)(AllPost);
and this is my postForm.js file:
import React, { Component } from 'react';
import { connect } from 'react-redux'
class PostForm extends Component {
handleSubmit = (e) => {
e.preventDefault();
const title = this.getTitle.value;
const message = this.getMessage.value;
const data = {
id: new Date(),
title,
message,
editing: false
}
console.log(data)
this.props.dispatch({
type: 'ADD_POST',
data,
});
this.getTitle.value = '';
this.getMessage.value = '';
}
render() {
return (
<div>
<h1>Create Post</h1>
<form onSubmit={this.handleSubmit}>
<input required type="text" ref={(input)=>this.getTitle = input}
placeholder="Enter Post Title"/>
<br /><br />
<textarea required rows="5" ref={(input)=>this.getMessage = input} cols="28"
placeholder="Enter Post" />
<br /><br />
<button>Post</button>
</form>
</div>
);
}
}
export default connect()(PostForm);
and this is my editComponent.js file
import React, { Component } from 'react';
import { connect } from 'react-redux';
class EditComponent extends Component {
handleEdit = (e) => {
e.preventDefault();
const newTitle = this.getTitle.value;
const newMessage = this.getMessage.value;
const data = {
newTitle,
newMessage
}
this.props.dispatch({ type: 'UPDATE', id: this.props.post.id, data: data })
}
render() {
return (
<div>
<form onSubmit={this.handleEdit}>
<input required type="text" ref={(input) => this.getTitle = input}
defaultValue={this.props.post.title} placeholder="Enter Post Title" /><br /><br />
<textarea required rows="5" ref={(input) => this.getMessage = input}
defaultValue={this.props.post.message} cols="28" placeholder="Enter Post" /><br /><br />
<button>Update</button>
</form>
</div>
);
}
}
export default connect()(EditComponent);
and this is my post.js file:
import React, { Component } from 'react';
import { connect } from 'react-redux'
class Post extends Component {
render() {
return (
<div>
<h2>{this.props.post.title}</h2>
<p>{this.props.post.message}</p>
<button onClick={() => this.props.dispatch({type: 'EDIT_POST', id: this.props.post.id})}>EDIT
</button>
<button onClick={ () => this.props.dispatch({type: 'DELETE_POST', id: this.props.post.id}) }>DELETE
</button>
</div>
);
}
}
export default connect()(Post);
and this is my postReducer.js file:
const postReducer = (state = [], action) => {
switch(action.type) {
case 'ADD_POST':
return state.concat([action.data]);
case 'DELETE_POST':
return state.filter((post)=>post.id !== action.id);
case 'EDIT_POST':
return state.map((post)=>post.id === action.id ? {...post,editing:!post.editing}:post)
case 'UPDATE':
return state.map((post)=>{
if(post.id === action.id) {
return {
...post,
title:action.data.newTitle,
message:action.data.newMessage,
editing: !post.editing
}
} else return post;
})
default:
return state;
}
}
export default postReducer;
Can anyone please help me to achieve this? I tried a lot to use same form form for both add and update and i failed to achieve this.
I think it's better you create separate component for rendering form data(FormComponent) and separate components for edit(EditComponent) and add(AddComponent).
This way there will not be clutter in one component and no if/else conditions for different modes like edit or add, or in future copy mode.
This approach will add flexibility and enhances compositional pattern of react.
1) AddComponent
import React, { Component } from 'react';
import { connect } from 'react-redux'
class AddComponent extends Component {
handleSubmit = (title, message) => {
const data = {
id: new Date(),
title,
message,
editing: false
}
this.props.dispatch({
type: 'ADD_POST',
data,
});
}
render() {
return (
<div>
<h1>Create Post</h1>
<FormComponent
buttonLabel='Post'
handleSubmit={this.handleSubmit}
/>
</div>
);
}
}
export default connect()(AddComponent);
2) EditComponent
import React, { Component } from 'react';
import { connect } from 'react-redux';
class EditComponent extends Component {
handleSubmit = (newTitle, newMessage) => {
const data = {
newTitle,
newMessage
}
this.props.dispatch({ type: 'UPDATE', id: this.props.post.id, data: data })
}
render() {
return (
<div>
<FormComponent
buttonLabel='Update'
handleSubmit={this.handleSubmit}
/>
</div>
);
}
}
export default connect()(EditComponent);
3) FormComponent
import React, { Component } from 'react';
class FormComponent extends Component {
handleSubmit = (e) => {
e.preventDefault();
const title = this.getTitle.value;
const message = this.getMessage.value;
this.props.handleSubmit(title, message);
}
render() {
return (
<form onSubmit={this.handleSubmit}>
<input required type="text" ref={(input) => this.getTitle = input}
defaultValue={this.props.post.title} placeholder="Enter Post Title" /><br /><br />
<textarea required rows="5" ref={(input) => this.getMessage = input}
defaultValue={this.props.post.message} cols="28" placeholder="Enter Post" /><br /><br />
<button>{this.props.buttonLabel}</button>
</form>
);
}
}
export default FormComponent;
Hope that helps!!!
You can create your own Form component with a prop of editMode to control whether it's Create or Update.
import React, {Component} from 'react';
import PropTypes from 'prop-types';
class Form extends Component {
handleSubmit = e => {
e.preventDefault();
const {editMode, post} = this.props;
const title = this.titleRef.value;
const body = this.bodyRef.value;
if (editMode){
const data = {
title,
body
}
this.props.dispatch({type: 'UPDATE', id: post.id, data})
}
else {
const data = {
id: new Date(),
title,
message,
editing: false
}
this.props.dispatch({type: 'ADD_POST', data});
}
}
render() {
const {editMode, post} = this.props;
const pageTitle = editMode ? 'Edit Post' : 'Create Post';
const buttonTitle = editMode ? 'Update' : 'Post';
return (
<div>
<h1>{pageTitle}</h1>
<form onSubmit={this.handleSubmit}>
<input
required
type="text"
ref={input => this.titleRef = input}
placeholder="Enter Post Title"
defaultValue={post.title}
/>
<textarea
required
rows="5"
ref={input => this.bodyRef = input}
cols="28"
placeholder="Enter Post"
defaultValue={post.body}
/>
<button>{buttonTitle}</button>
</form>
</div>
);
}
}
Form.propTypes = {
editMode: PropTypes.bool,
post: PropTypes.object
}
Form.defaultProps = {
editMode: false, // false: Create mode, true: Edit mode
post: {
title: "",
body: ""
} // Pass defined Post object in create mode in order not to get undefined objects in 'defaultValue's of inputs.
}
export default Form;
It would be on create mode by default but if you wanna update the post you should pass editMode={true} to your form component.

how to show a new user on the page?

I'm a Junior. I could not find information on the Internet and could not solve it myself. I use for my app the react with axios. Through the post request I want to add a new user. How to show a new user on the page? not in the console.
import React from "react"; import axios from "axios";
class PersonInput extends React.Component {
state = {
name: '',
}
handleChange = (event) => {
this.setState({name: event.target.value});
}
handleSubmit = (event) => {
event.preventDefault();
const user = {
name: this.state.name
};
axios.post(`https://jsonplaceholder.typicode.com/users`, {user}).then((res) => {
console.log(res);
})
}
render() {
return (
<div>
<form onSubmit={this.handleSubmit}>
<label>
User Name:
<input type="text" name="name" onChange={this.handleChange} />
</label>
<button type="submit">Add user</button>
<ul>{this.state.res}</ul>
</form>
</div>
);
}
}
export default PersonInput;
You didn't need to make a post request for it.
Try this:
import React from "react"; import axios from "axios";
class PersonInput extends React.Component {
state = {
name: '',
users: ''
}
handleChange = (event) => {
this.setState({name: event.target.value});
}
handleSubmit = (event) => {
event.preventDefault();
this.setState({users: this.state.name});
}
render() {
return (
<div>
<form onSubmit={this.handleSubmit}>
<label>
User Name:
<input type="text" name="name" onChange={this.handleChange} />
</label>
<button type="submit">Add user</button>
<ul>{this.state.users}</ul>
</form>
</div>
);
}
}
export default PersonInput;
Define a new key to your state and when u get response store it in that key
state = {
name: '',
res: []
}
onSubmit = async e => {
e.preventDefault();
const data = { name: this.state.name };
const response = await axios.post('https://jsonplaceholder.typicode.com/users', data);
this.setState({res: response.data});
}
And use async await it is easier and cleaner. Just a personal preference

Resources