React onClick function call refreshes app - javascript

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);
}

Related

The first task in my React Js tasklist project is always undefined

I have been creating a react js project which is a task list. However I have been facing an error, for some reason I have found multiple errors. First for debugging purposes I created a handle delete function which will log a string to the console to show that it has been triggered. It is only meant to be triggered when the X button on the task element is clicked, however, it triggers whenever I submit the text input to create a new task. Secondly the biggest error is that the first element is always undefined no matter what I type. How do I fix this problem.
import React, { Component } from "react";
import ReactDOM from "react-dom";
import TaskList from "./index2";
class Tests extends Component {
constructor(props) {
super(props);
this.state = {
value: "",
object: {},
object2: [],
};
this.updateInputValue = this.updateInputValue.bind(this);
this.submit = this.submit.bind(this);
}
updateInputValue(evt) {
this.setState({
value: evt.target.value,
});
}
submit() {
if (this.state.value.trim() != "") {
this.setState({
object: { task: this.state.value.trim(), id: Date.now },
object2: [...this.state.object2, this.state.object],
value: "",
});
} else {
return;
}
}
render() {
return (
<div>
<label for="text">Text 1</label>
<br />
<input
type="text"
label="text"
onChange={(evt) => this.updateInputValue(evt)}
onSubmit={this.submit}
value={this.state.value}
/>
<br />
<button onClick={this.submit} style={{ height: 50, width: 60 }}>
Submit
</button>
<br></br>
<TaskList
allTasks={this.state.object2}
handleDeleteFunction={console.log("handle delete has been triggered")}
/>
</div>
);
}
}
export default Tests;
ReactDOM.render(<Tests />, document.getElementById("root")
);
And my taskList code
import React from "react";
import "./woah.css";
export default function TaskList({ allTasks, handleDeleteFunction }) {
return (
<ul>
{allTasks.map(({ task, id }) => (
<li key={id} id="task">
<p>{task}</p>
<button onClick={() => handleDeleteFunction}>X</button>
</li>
))}
</ul>
);
}
Try this:
submit() {
if (this.state.value.trim() != "") {
const newObj = { task: this.state.value.trim(), id: Date.now };
this.setState({
object: newObj,
object2: [...this.state.object2, newObj],
value: "",
});
} else {
return;
}
}
You are referencing the current object in state, which is empty.

Sending a form refreshes page and not posting to MongoDb Database

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
}

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
}

Type Error property undefined even though action fired off defining it

I am currently having a button redirect to the same location a button on a prior dashboard takes you to it is essentially a render of a graph and all of its notes and questions rendered. Now the specific snippet in the error works from my initial location which is denoted below in my screenshot that speculation button will take you to the rendered show route below it. However, the button that appears after you have made a new question and note do not they use the same snippet but one errors out I am also adding the snippet for the buttons.
*edit 1 adding show.js
show.js
// built-in(lifecycle) methods imported here
import React, { Component } from 'react'
// import components from local
import Graph from '../components/Graph'
import Notes from '../components/Notes'
import Questions from '../components/Questions'
//imbrl allows us to enable routing by updating url and rendering needed component listed in routeer
import { NavLink } from 'react-router-dom'
//bootstrap WIP
import Button from 'react-bootstrap/Button'
import Row from 'react-bootstrap/Row'
import Col from 'react-bootstrap/Col'
import Card from 'react-bootstrap/Card'
// access state from redux store
import { connect } from 'react-redux'
class Show extends Component {
render() {
// variables for objects
const graph = this.props.graphs.find(graph => { return graph.id === parseInt(this.props.match.params.id)})
const notes = this.props.notes.filter(note => note.graph.id === graph.id)
const questions = this.props.questions.filter(question => question.graph.id === graph.id)
// if graph exists it loads all corresponding notes and questions with it
if (graph) {
return (
<Row>
<Col md={3}>
<Notes graph={graph} notes={notes} />
</Col>
<Col md={6} >
<Card>
<Graph graph={graph}/>
</Card>
<NavLink
to={`/graphs/${graph.id}/interact`}>
<Button>Interact</Button>
</NavLink>
</Col>
<Col md={3}>
<Questions graph={graph} questions={questions} />
</Col>
</Row>
)
} else {
return (
<div>
<NavLink
style={{marginRight: '10px'}}
to="/">
<Button variant="dark" size="lg" block>Add Data to get started</Button>
</NavLink>
</div>
)
}
}
}
// this will need access to the objects
const mapStateToProps = state => {
return {
graphs: state.graphs,
notes: state.notes,
questions: state.questions
}
}
export default connect (mapStateToProps)(Show)
graphinput.js
import React, { Component } from 'react'
// import actions future?
import { addNote } from '../actions/addSpeculations'
import { addQuestion} from '../actions/addSpeculations'
// browser url routing render component as needed
import { NavLink } from 'react-router-dom'
// import local components
import Note from '../components/Note'
import Question from '../components/Question'
// bootstrap styling
import Button from 'react-bootstrap/Button'
import Form from 'react-bootstrap/Form'
// allows access to redux store this is a stateful component
import { connect } from 'react-redux'
class GraphInput extends Component {
// initial state placejolders
state ={
note: {
content: ""
},
question: {
content: ""
},
visible: false,
view: false
}
// state will be updated everytime the form value changes
handleChange = (event) => {
this.setState({
[event.target.name]: {content: event.target.value, graph_id: this.props.graph_id}
})
}
// visible key will show as true to confirm before calling fetch.
handleSubmit = (event) => {
event.preventDefault()
this.setState({visible: true})
}
handleSave = () => {
this.props.addNote(this.state.note)
this.props.addQuestion(this.state.question)
this.setState({
note: {content: ""},
question: {content: ""},
visible: false,
view: true
})
}
// if user cancels submission, state should reset to initial values
handleCancel = () => {
this.setState({
note: {content: ""},
question: {content: ""},
visible: false,
view: false
})
}
render () {
// Need to check current state to base what return should be used for what user interaction has done.
/*const validated= this.state.note.content.length > 20 && this.state.note.content.length > 20*/
if (this.state.visible === false && this.state.view === false){
/// render form and graph
return (
<div>
<h3> Add your Observations or Speculation below. </h3>
<Form onSubmit={event => this.handleSubmit(event)} >
<Form.Group>
<Form.Control size="lg" type="text" name="note" placeholder="Make an observation." value={this.state.note.content} onChange={event => this.handleChange(event)} />
</Form.Group>
<Form.Group>
<Form.Control size="lg" type="text" name="question" placeholder="Ask a question" value={this.state.question.content} onChange={event => this.handleChange(event)} />
</Form.Group>
<Button type="submit" >Add</Button>
</Form>
</div>
)
} else if (this.state.visible === true && this.state.view === false) {
/// rwendering draft to confirm submission
return (
<div>
<Note note={this.state.note} />
<Question question={this.state.question} />
<Button type="submit" onClick={this.handleSave}> Save Speculation to Database</Button>
<Button type="submit" variant="danger" onClick={this.handleCancel}>Cancel</Button>
</div>
)
} else if (this.state.view === true) {
// after saving, user can now navigate to view all
return (
<NavLink
style={{ marginRight: '10px'}, {backgroundColor: 'transparent'}}
to={`/graphs/${this.props.graph_id}/speculations`}
graph={this.props.graph}>
<Button size="lg" block>View All Speculation for This Graph</Button>
</NavLink>
)
}
}
}
// only state needed is graph speculations for specific graph
const mapStateToProps = (state) => {
return {
graph: state.graph
}
}
// Dispatch to props for Notes and Questions
export default connect(mapStateToProps, {addNote, addQuestion})(GraphInput)
Broken Button
<NavLink
style={{ marginRight: '10px'}, {backgroundColor: 'transparent'}}
to={`/graphs/${this.props.graph_id}/speculations`}
graph={this.props.graph}>
<Button size="lg" block>View All Speculation for This Graph</Button>
</NavLink>
Working Button
<NavLink
style={{ marginRight: '10px'}}
to={`/graphs/${this.props.graph.id}/speculations`}
url={this.props.graph.screenshot_url} >
<Button variant="success" >
Speculations
</Button>
</NavLink>
This is the button that works
This is where the button should take you
This button does not go to the show with that specific graph's notes and questions. (Both are using the same snippet to identify graph and its id or are supposed to)

Conditional rendering based on user input

I've just started learning React and am struggling with conditional rendering. I want to render components based on form input but i'm not sure what needs to be done or where it needs to be executed.
I have imported my Form component which has the input I want to use and have another component like this:
import React, {Component} from 'react';
import Form from './Form';
import CardOne from './CardOne';
import CardTwo from './CardTwo';
import CardThree from './CardThree';
export default class CardContainer extends Component {
render(){
return (
<div>
<CardOne />
<CardTwo />
<CardThree />
</div>
)
}
}
I basically want to be able to show certain Cards if the value of the input is greater than X when the form is submitted, but I don't know how to target an imported component.
This is my Form component:
export default class Form extends Component {
state = {
number: ''
};
change = (e) => {
this.setState({
[e.target.name]: e.target.value
});
};
onSubmit = e => {
e.preventDefault();
this.props.onSubmit(this.state);
this.setState({
number: ''
})
};
render(){
return (
<form>
<label>Number</label>
<input
type="number"
name="number"
placeholder="Number"
value={this.state.number}
onChange={e => this.change(e)} />
<button onClick={e => this.onSubmit(e)}>Submit</button>
</form>
)
}
}
Any help will be massively appreciated!
I have redesigned your Form component , Below is my code. . Let me know if u faced any issues on that .
import React, { Component } from 'react';
import CardOne from './CardOne';
import CardTwo from './CardTwo';
import CardThree from './CardThree';
export default class Form extends Component {
state = {
number: '',
showOne:true,
showTwo:false,
showThree:false,
userInputValue:''
};
change = (e) => {
this.setState({
userInputValue: e.target.value
});
};
onSubmit = e => {
e.preventDefault();
this.props.onSubmit(this.state);
if (this.state.userInputValue > 10 && this.state.userInputValue <20 ){
this.setState({
showTwo: true,
})
}
if (this.state.userInputValue > 20 && this.state.userInputValue < 30) {
this.setState({
showThree: true,
})
}
};
render() {
return (
<div>
<form>
<label>Number</label>
<input
type="number"
name="number"
placeholder="Number"
value={this.state.userInputValue}
onChange={e => this.change(e)} />
<button onClick={e => this.onSubmit(e)}>Submit</button>
</form>
<div>
{this.state.showOne ?
<CardOne />
:
<div></div>
}
{this.state.showTwo ?
<CardTwo />
:
<div></div>
}
{this.state.showThree ?
<CardThree />
:
<div></div>
}
</div>
</div>
)
}
}
// What i wrote above is your base functionality . You reedit the condition depends on ur requirement .
This is what I came up with following your logic of card rendering. I did not change Form coponent but rather worked on the Container
export default class CardContainer extends Component {
constructor(props) {
super(props);
state = {
number: 0,
}
this.onFormSubmit = this.onFormSubmit.bind(this);
}
onFormSubmit=(number)=>{
this.setState({ number: number });
}
render(){
let i=Math.floor(this.state.number/10)
return (
<div>
<Form onSubmit={() => this.onFormSubmit(number)}
[<CardOne />, <CardTwo />, <CardThree/>].slice(0,i).map(card =>
{card}
)
</div>
)
}
}
I would use render prop for this kind of problem. You can research more about render props but basically your CardContainer component will not render those cards component statically as it is. It will return props.children instead.
And then you will have a function (i.e function TestX) that will have a conditional to check what the value of X is. This is the function that will return either , , based on what X is. The function TestX will receive props from CardContainer, including the value of X that is read from the state.
So I will just use CardContainer component with as its child.

Categories