Sending a form refreshes page and not posting to MongoDb Database - javascript

Problem 1: Content not showing after the last Else statement in index.jsx file for the form, fixing it would be deleting the last Else statement but this leads to another problem.
Problem 2: After pressing the Send Button in the mail form, the page refreshes but nothing shows in either database nor the message that Mail Successfully sent.
i hope someone can help me. No Error Messages in console
Here's all the files:
index.jsx(mail):
import React, { Component } from "react";
import * as mailActions from "../../store/actions/mail";
import { connect } from "react-redux";
import Loader from "../Loader";
import TextField from "#material-ui/core/TextField";
import SendIcon from "#material-ui/icons/Send";
import AddIcon from "#material-ui/icons/Add";
import "./style.css";
class CreateMail extends Component {
state = {
createMail: false,
};
createMailHandler = () => {
this.setState((prevState) => {
return {
createMail: !prevState.createMail,
};
});
};
render() {
let content;
if (this.props.loading) {
content = <Loader />;
} else if (this.props.error) {
content = (
<div>
{this.props.errorMessages.map((error, i) => (
<p key={i}>{error}</p>
))}
</div>
);
} else if (this.props.mailSent) {
content = <p>Mail sent successfully!</p>;
} else {
content = (
<form className="createMailForm">
<div className="formControl">
<TextField
id="receiver"
label="Receiver"
type="text"
name="receiver"
placeholder="Username of receiver"
/>
</div>
<div className="formControl">
<TextField
label="Subject"
type="text"
name="subject"
placeholder="Subject"
id="subject"
/>
</div>
<div className="formControl">
<TextField id="body" label="Body of the Mail" multiline rows={6} />
</div>
<button className="createMailSendBtn">
<SendIcon />
</button>
</form>
);
}
return (
<>
<div className="createMailContainer" onClick={this.createMailHandler}>
<button className="createMailCreateBtn">
<AddIcon
className={`${
this.state.createMail ? "createMailCreateBtn--close" : ""
}`}
/>
</button>
</div>
<div
className={`createMailFormContainer ${
this.state.createMail
? "formContainer--visible"
: "formContainer--hidden"
}`}
>
{content}
</div>
</>
);
}
}
const mapStateToProps = (state) => {
return {
...state.mail,
};
};
const mapDispatchToProps = (dispatch) => {
return {
mailInit: (mailObj) => dispatch(mailActions.mailInit(mailObj)),
};
};
export default connect(mapStateToProps, mapDispatchToProps)(CreateMail);

Your Problem 2 is because of this:
<form className="createMailForm">
you have to pass a submit handler method here and that method must hold the form submit like:
<form className="createMailForm" onSubmit={this.submitHandler}>
and
submitHandler = (e) => {
e.preventDefault();
// Now perform your action here like make api call or call some action etc
}

Related

Cannot read properties of undefined (reading *)

Hey I am learning reactjs as much as i have learned I am trying to make note app
my code given below
my App.js file
import React , {useEffect, useState} from "react"
import { nanoid } from "nanoid"
import Editor from './Note/Editor'
import Sidebar from "./Note/Sidebar"
function App() {
const [notes , setNotes] = useState(JSON.parse(localStorage.getItem("notes"))||[])
const [currentNoteID , setCurrentNoteID] = useState(false)
useEffect(()=>{
localStorage.setItem("notes" , JSON.stringify(notes))
},[notes])
function createNewNotes(){
const newNotes = {
id: nanoid(),
title:"untitled",
body: "sdasda",
lastModified: Date.now()
}
setNotes(prevNote => [newNotes , ...prevNote])
setCurrentNoteID(newNotes.id)
}
function deleteNote(noteID){
setNotes(prevNote => prevNote.filter(note=> note.id !== noteID ))
}
function getNotes(){
return notes.find((note)=> note.id === currentNoteID)
}
return (
<div className="note">
<Sidebar
notes={notes}
createNewNotes={createNewNotes}
currentNoteID={currentNoteID}
setCurrentNoteID={setCurrentNoteID}
deleteNote={deleteNote}
/>
<Editor
notes={getNotes()}
currentNoteID={currentNoteID}/>
</div>
);
}
export default App;
my Sidebar.js file
import React from 'react'
import './style.css'
export default function Sidebar(props){
return(
<>
<div className='sidebar' >
<div className='sidebar-header'>
<h3>Notes</h3>
<button className='add' onClick={props.createNewNotes} >Add</button>
</div>
{ props.notes.map((note)=>{
return(
<div key={note.id}
className={`${note.id===props.currentNoteID ? "active" : ""}`}
onClick={()=>props.setCurrentNoteID(note.id)}
>
<div>
<div className="sidebar-tab">
<div className='sidebar-title'>
<p className='title'>Untitled</p>
<button className='delete' onClick={()=>props.deleteNote(note.id)}>Delete</button>
</div>
<p className='note-preview'>summary of text</p>
</div>
</div>
</div>
)
})}
</div>
</>
)
}
my Editor.js file
import React , {useState} from "react";
import './style.css'
export default function Editor(props){
const [edit , setEdit] = useState(props.notes)
function handleChange(event){
const {name , value} = event.target
setEdit(prevNote=> {
return {
...prevNote,
[name] : value
}
})
}
if(!props.currentNoteID)
return <div className="no-note">no note active</div>
return(
<>
<div className="main">
<input type="text" className="main-input" name="title" placeholder="Enter title here" value={edit.title} onChange={handleChange} autoFocus/>
<textarea className="main-textarea" name="body" placeholder="Type your notes" value={edit.body} onChange={handleChange} />
<div className="preview">
<h1 className="preview-title">{edit.title}</h1>
<div className="main-preview">{edit.body}</div>
</div>
</div>
</>
)
}
whenever i click add button or any sidebar button it shows me error
Uncaught TypeError: Cannot read properties of undefined (reading 'title')
please help me out how to fix this issue
You're expecting getNotes (which should probably be named getActiveNote, IMHO) to re-run every time notes or currentNoteID change.
To achieve this, you have to declare it as a callback (useCallback) and to declare its dependencies. Also you want to place the result in state (e.g: activeNote):
const getActiveNote = useCallback(
() => notes.find((note) => note.id === currentNoteID),
[notes, currentNoteID]
);
const [activeNote, setActiveNote] = useState(getActiveNote());
useEffect(() => {
setActiveNote(getActiveNote());
}, [getActiveNote]);
// ...
<Editor note={activeNote} />
... at which point, you no longer need the currentNoteID in the <Editor /> as you can get it from props.note.id.
See it working here: https://codesandbox.io/s/crazy-glade-qb94qe?file=/src/App.js:1389-1448
Note: the same thing needs to happen in <Editor>, when note changes:
useEffect(() => setEdit(note), [note]);

How to mirror the state from switch button with ReactJS?

I'm still a beginner with ReactJS and I need to mirror my switch button in my application.
My switch button change the language from my application, and I have the same button in the header and footer of my pages.
When I change the language from the site, the another button doesn't change at the same time, for example, if I click in my header component to change the language, the button in my footer stay the same way.
I put this example into codesandbox.io
Can you tell me how do I fix the buttons?
import "./styles.scss";
import { I18nProvider } from "./providers/i18n";
import ToggleLanguage from "./components/ToggleLanguage/ToggleLanguage.js";
export default function App() {
return (
<I18nProvider>
<div className="App">
<h3>Example Header</h3>
<ToggleLanguage />
<div style={{ margin: "20px 0" }} />
<h3>Example Footer</h3>
<ToggleLanguage />
</div>
</I18nProvider>
);
}
import React from "react";
import "./ToggleLanguage.scss";
// providers
import { AppContext } from "../../providers/app";
import { saveToStorage } from "../../utils/localStorage";
const ToggleLanguage = () => {
const { state, dispatch } = React.useContext(AppContext);
const onToggleSiteLang = (siteLang) => () => {
dispatch({ type: "setLang", siteLang });
saveToStorage("siteLang", siteLang);
};
return (
<div className="toggle-language">
<label className="switch">
<input
onChange={() => onToggleSiteLang(state.siteLang)}
className="switch-checkbox"
type="checkbox"
/>
<div className="switch-button" />
<div className="switch-labels">
<span>PT</span>
<span>EN</span>
</div>
</label>
</div>
);
};
export default ToggleLanguage;
Thank you very much in advance for any help/tip.
Just control your checkbox with your state. checked={state.siteLang === 'en'}
import React from "react";
import "./ToggleLanguage.scss";
// providers
import { AppContext } from "../../providers/app";
import { saveToStorage } from "../../utils/localStorage";
const ToggleLanguage = () => {
const { state, dispatch } = React.useContext(AppContext);
const onToggleSiteLang = (siteLang) => () => {
dispatch({ type: "setLang", siteLang });
saveToStorage("siteLang", siteLang);
};
return (
<div className="toggle-language">
<label className="switch">
<input
onChange={() => onToggleSiteLang(state.siteLang)}
className="switch-checkbox"
type="checkbox"
checked={state.siteLang === 'en'}
/>
<div className="switch-button" />
<div className="switch-labels">
<span>PT</span>
<span>EN</span>
</div>
</label>
</div>
);
};
export default ToggleLanguage;
Let me know if this works for you

React onClick function call refreshes app

I'm making a simple note taking app in the style of google keepnote. When I press the the add button from the create are component it calls the function in the app component that will add the note to an array for rendering. The function call works just fine but then the whole app refreshes and any data is lost.
I cant work out what is causing the refresh.
the App component:
import React, { useState } from "react";
import Header from "./Header";
import Footer from "./Footer";
import Note from "./Note";
import CreateArea from "./CreateArea";
function App() {
const [notes, setNotes] = useState({ id: "", title: "", content: "" });
function addNote(note) {
console.log(note);
}
return (
<div>
<Header />
<CreateArea newNote={addNote} />
<Note key={1} title="Note title" content="Note content" />
<Footer />
</div>
);
}
export default App;
The CreateArea component:
import React, { useState } from "react";
function CreateArea(props) {
const [note, setNote] = useState({ title: "", content: "" });
function handleChange(event) {
if (event === null || event === undefined) return;
const { name, value } = event.target;
if (name === "title") {
setNote(prevValue => {
return {
title: value,
content: prevValue.content
};
});
} else {
setNote(prevValue => {
return {
title: prevValue.title,
content: value
};
});
}
}
return (
<div>
<form>
<input onChange={handleChange} name="title" placeholder="Title" />
<textarea
onChange={handleChange}
name="content"
placeholder="Take a note..."
rows="3"
/>
<button
onClick={() => {
props.newNote(note);
}}
>
Add
</button>
</form>
</div>
);
}
export default CreateArea;
Any help will be greatly appreciated.
This is because you are clicking a button in a form. The form is trying to submit, which refreshes the page. You can add e.preventDefault to stop that.
<button
onClick={(e) => {
e.preventDefault()
props.newNote(note);
}}
>
You need to prevent the default behavior of form submission.
In your OnClick event pass the event as an argument:
<button
onClick={(e) => {
props.newNote(e,note);
}}
>
Add
</button>
and while calling this function on parent component:
function addNote(e,note) {
e.preventDefault() // prevent the default behavior
console.log(note);
}

componentDidUpdate load API infinity times

I'm passing information from Component A from the Component B. After that depending on the props id I'm calling an API and setting the data to states. However, when I called the setState parameter to set the the API loaded data, the API were been called contentiously. Here's the Component B code:
import React, { Component } from 'react';
import { connect } from 'react-redux';
import Modal from "react-bootstrap/Modal";
import PropTypes from 'prop-types'
import axios from 'axios';
import TextBox from '../../layouts/textBox'
import { getPermission } from '../../actions/permissionActions';
class Form extends Component {
state = {
editSelectedPermissions: []
}
async componentDidMount() {
this.props.getPermission();
}
async componentDidUpdate() {
const roleId = this.getRoleId();
if (roleId) {
const res = await axios.get(`http://localhost:3000/v1/permissions/role/${roleId}/`);
console.log(res.data.data);
if ( res.data.data.permission.length != 0) {
this.setState({
editSelectedPermissions: res.data.data.permission
})
}
}
}
getRoleId=()=> this.props.data.id
render() {
const { onCloseModal, onSubmit, onChange, onCheckBox, permissions } = this.props;
const { showModal, id, name, description} = this.props.data;
const { editSelectedPermissions } = this.state;
let selectedPermission = false;
return (
<div>
<Modal show={showModal} centered onHide={onCloseModal}>
<Modal.Header closeButton>{id ? "Edit" : "Add"} User Role</Modal.Header>
<Modal.Body>
<form onSubmit={onSubmit.bind(this)}>
<input type="hidden" name="id" value={id} />
<div className="form-row">
<div className="col-md-6">
<TextBox type="text" name="name" placeholder="Enter Name" label="Name" value={name} onChange={onChange} />
</div>
<div className="col-md-6">
<TextBox type="text" name="description" placeholder="Enter Description" label="Description" value={description} onChange={onChange} />
</div>
</div>
{permissions.map((item, index) => {
if (editSelectedPermissions.length > 0)
selectedPermission = editSelectedPermissions.find((item2) => item2.id === item.id)
return (
<div className="form-check" key={index}>
<input className="form-check-input" type="checkbox" name="permission" checked={selectedPermission} onChange={onCheckBox} value={item.id}/>
<label className="form-check-label" htmlFor="defaultCheck1">
{item.name}
</label>
</div>
)
})}
<div className="d-flex justify-content-center">
<input
type="submit"
className="btn btn-primary"
value={id ? "Edit Record" : "Create Record"}
/>
</div>
</form>
</Modal.Body>
</Modal>
</div>
);
}
}
Form.propTypes = {
getPermission: PropTypes.func.isRequired,
}
const mapStateToProps = (state) => ({
permissions: state.permission.permissions
});
export default connect(mapStateToProps, {getPermission} )(Form);
Any reason why it's been called continuously?
componentDidUpdate run each time state or props change. Because you setState inside, after it it will run again, change state again, and run again infinitely. Add checker before setState
if ( res.data.data.permission.length != 0 && this.state.editSelectedPermisssions != res.data.data.premission) {
this.setState({
editSelectedPermissions: res.data.data.permission
})
}
Call API in componentDidMount cycle rather than in componentDidUpdate.
It's because
if (roleId) //always true
this statement is always true.
Maybe you could store current roleId and and do the comparision
if (this.state.currentRoleId !== roleId) {
const res = await axios.get(`http://localhost:3000/v1/permissions/role/${roleId}/`);
console.log(res.data.data);
if ( res.data.data.permission.length != 0) {
this.setState({
currentRoleId: roleId,
editSelectedPermissions: res.data.data.permission
})
}
}
It is simply because your component update frequently as such it keeps making the API call and this is because you are making the API call in componentDidUpdate. Usually, you make the API call in componentDidMount, this will make the API call once.
async componentDidMount() {
this.props.getPermission();
const res = await axios.get(`http://localhost:3000/v1/permissions/role/${roleId}/`); // should be make here
}
Not in
componentDidUpdate(){
//Avoid Making API calls here
}

TypeError: songs.map is not a function

Hi I'm new to react Every time when i run the code it shows the same error. Can anyone explain what is wrong with this code.
import React, { Component } from 'react'
import '../Search.css'
import axios from 'axios'
export default class Search extends Component {
state = {
songs: [],
};
componentDidMount(key) {
axios.get(`https://cors-anywhere.herokuapp.com/https://itunes.apple.com/search?term=`+key).then(res => {
console.log(res);
this.setState({songs: res.data});
});
}
render() {
const {songs} = this.state;
return (
<div className="container-fluid">
<input
type="text"
name="search"
placeholder="Search..."
id="search"
onChange={(event) => this.componentDidMount(event.target.value)}
/>
<div>
{songs.map(song => (
<h1>{song.artistName}</h1>
))}
</div>
</div>
)
}
}
The map method is available only for an Array type. This means that res.data is not an array. Please inspect the response coming from the request URL first.
You should display the data only when it's available:
render() {
const {songs} = this.state;
return (
<div className="container-fluid">
<input
type="text"
name="search"
placeholder="Search..."
id="search"
onChange={(event) => this.componentDidMount(event.target.value)}
/>
<div>
{songs && songs.map(song => (
<h1>{song.artistName}</h1>
))}
</div>
</div>
)
}
}

Categories