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
}
Related
I am trying to post new information about a cow to my cow API, however, everytime i hit the submit button on my frontend, it seems to be sending an empty object rather than the name of the cow, description of the cow, and image of the cow (via url). What is causing it to send an empty object versus my desired data?
Here is the frontend code:
import React, { useEffect, useState } from 'react';
import axios from 'axios';
import './App.css';
const baseUrl = "http://localhost:3001/api/cows"
function Display({setNameOfCow, setImageOfCow, setDescriptionOfCow, nameOfCow, imageOfCow, descriptionOfCow}) {
axios.get(baseUrl)
.then(res => res.data)
.then(res => {
setNameOfCow(res.name)
setImageOfCow(res.image)
setDescriptionOfCow(res.description)
})
return (
<div>
<p>{nameOfCow}</p>
<img src={imageOfCow}/><p>{descriptionOfCow}</p>
</div>
)
}
function Input({setNameOfCow, setImageOfCow, setDescriptionOfCow, nameOfCow, imageOfCow, descriptionOfCow}) {
function handleSubmit(e) {
e.preventDefault()
let newObject = {
name: nameOfCow,
description: descriptionOfCow,
image: imageOfCow
}
axios.post(baseUrl, newObject)
}
return (
<div>
<form>
<label htmlFor="name">name: </label>
<input type="text" id="name" onChange={(e) => {
const eTarget = e.target.value
setNameOfCow(eTarget)}}/><br></br>
<label htmlFor="description">description: </label>
<input type="text" id="description" onChange={(e) => {
const eTargetDesc = e.target.value
setDescriptionOfCow(eTargetDesc)}}/><br></br>
<label htmlFor="image">image url: </label>
<input type='text' id="image" onChange={(e) => {
const eTargetImage = e.target.value
setImageOfCow(eTargetImage)}}/><br></br>
<button type="submit" onSubmit={handleSubmit}>Add a cow!</button>
</form>
</div>
)
}
function App() {
const [nameOfCow, setNameOfCow] = useState('')
const [descriptionOfCow, setDescriptionOfCow] = useState('')
const [imageOfCow, setImageOfCow] = useState('')
return (
<div className="App">
<Input imageOfCow={imageOfCow} setNameOfCow={setNameOfCow} setDescriptionOfCow={setDescriptionOfCow} setImageOfCow={setImageOfCow} />
<Display setNameOfCow={setNameOfCow} setImageOfCow={setImageOfCow} setDescriptionOfCow={setDescriptionOfCow} nameOfCow={nameOfCow} imageOfCow={imageOfCow} descriptionOfCow={descriptionOfCow} />
</div>
);
}
export default App
and here is the image showing the empty objects being posted:
Looking into your Input component props:
function Input({setNameOfCow, setImageOfCow, setDescriptionOfCow, nameOfCow, imageOfCow, descriptionOfCow}) {...
We can see that you missing to pass this props when using this component:
<Input imageOfCow={imageOfCow} setNameOfCow={setNameOfCow} setDescriptionOfCow={setDescriptionOfCow} setImageOfCow={setImageOfCow} />
The correct way to use is something like:
<Input
imageOfCow={imageOfCow}
nameOfCow={nameOfCow}
descriptionOfCow={descriptionOfCow}
setNameOfCow={setNameOfCow}
setDescriptionOfCow={setDescriptionOfCow}
setImageOfCow={setImageOfCow}
/>
Also the correct way to prevent the form default behavior is setting the onSubmit and the handleSubmit at the form attribute (you can remove from the button):
<form onSubmit={handleSubmit}>
Otherwise a very nice change is to put your axios request inside a useEffect hook to prevent your app from making request every time it re-render.
Using something like this the app will make the request only at the first component render.
const getCow = async (baseUrl) => {
const cow = await axios.get(baseUrl);
setNameOfCow(cow.name);
setImageOfCow(cow.image);
setDescriptionOfCow(cow.description);
};
useEffect(() => {
getCow(baseUrl);
}, []);
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
}
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>
)
}
}
I am trying to create a customer details form in react (currently using react-json-form) where I can reuse the values in the inputs to create a saved file that the app can refer to. I have created the form and can output the results but I am unsure how to save the input values for future use or call them back once they are saved.
If anyone has any suggestions or examples of a form that does this then I would be greatly appreciative.
My code is as follows:
import React, { Component } from 'react';
import JSONTree from 'react-json-tree';
import { BasicForm as Form, Nest, createInput } from 'react-json-form';
const Input = createInput()(props => <input type="text" {...props} />);
const UserFields = () => (
<section>
<h3>User</h3>
<div>Name: <Input path="name" /></div>
<div>Email: <Input path="email" /></div>
</section>
);
export default class ExampleForm extends Component {
state = { data: {} };
updateData = data => this.setState({ data });
render() {
return (
<Form onSubmit={this.updateData}>
<Nest path="user">
<UserFields />
</Nest>
<button type="submit">Submit</button>
<JSONTree data={this.state.data} shouldExpandNode={() => true} />
</Form>
);
}
}
A more simple solution would be to use a form, like a semanti-ui-react form, store the information to the state onChange, then convert the info to JSON for storage.
import { Form, Button } from 'semantic-ui-react'
export default class App extends Component {
constructor() {
super()
this.state = {
name: "",
email: ""
}
}
handleChange = (e, {name, value}) => {
console.log(name, value)
this.setState({[name]: value})
}
render() {
return (
<div>
<Form onSubmit={this.sendDataSomewhere}>
<Form.Field>
<Form.Input name="name" value={this.state.name} onChange={this.handleChange}/>
</Form.Field>
<Form.Field>
<Form.Input name="email" value={this.state.email} onChange={this.handleChange}/>
</Form.Field>
<Button type="submit">Submit</Button>
</Form>
</div>
)
}
}
I use a dynamic method of receiving the input from different fields using the name and val attributes. The values captured in state are then accessible by this.state.whatever
Hope this helped
I have a usecase that there is a form which should be controlled one and should have its field be pre-populated so that user can edit the form. For this what i have done is
const mapStateToProps = createStructuredSelector({
myInfo: makeSelectMyInfo(),
errorResponse: makeSelectMyInfoErrorResponse()
});
const mapDispatchToPropes = dispatch => ({
loadMyInfo: () => dispatch(getMyInfo()),
updateMyInfo: (myInfo, token) => dispatch(updateMyInfo(myInfo, token))
});
class ConfirmPropertyByUser extends React.PureComponent {
constructor(props) {
super(props);
this.state = {
user_info: {
contact_fname: "",
contact_lname: "",
agree_terms_condition: false,
}
};
}
componentDidMount() {
this.props.loadMyInfo();
}
componentWillReceiveProps(nextProps) {
if (nextProps.myInfo !== this.props.myInfo) {
console.log("object", Object.values(nextProps.myInfo));
this.setState(state => ({
user_info: {
...state.user_info,
contact_fname: nextProps.myInfo.contact_fname,
contact_lname: nextProps.myInfo.contact_lname,
}
}));
}
}
handleChange = e => {
this.setState({
user_info: { ...this.state.user_info, [e.target.name]: e.target.value }
});
};
handleUserTerms = e =>
this.setState({
user_info: {
...this.state.user_info,
agree_terms_condition: e.target.checked
}
});
handleSubmit = e => {
e.preventDefault();
this.props.updateMyInfo(this.state.user_info, this.props.match.params.id);
};
render() {
const { errorResponse } = this.props;
const { user_info } = this.state;
let message;
if (errorResponse && typeof errorResponse === "string") {
message = <Notification message={errorResponse} timeout={5000} />;
}
return (
<div className="container">
{message && message}
<div className="card card-lg">
<h1>Register</h1>
<form onSubmit={this.handleSubmit}>
<div className="form-group">
<label>First Name</label>
<input
type="text"
name="contact_fname"
className="form-control"
value={user_info && user_info.contact_fname}
onChange={this.handleChange}
/>
</div>
<div className="form-group">
<label>Last Name</label>
<input
type="text"
name="contact_lname"
className="form-control"
value={user_info && user_info.contact_lname}
onChange={this.handleChange}
/>
</div>
<div className="form-group">
<input
className="custom-control-input"
type="checkbox"
onChange={this.handleUserTerms}
/>
</div>
<button
className="btn btn-default btn-block btn-lg"
disabled={
!user_info.password || !user_info.agree_terms_condition
}
>
Submit Details
</button>
</fieldset>
</form>
</div>
</div>
);
}
}
export default connect(mapStateToProps, mapDispatchToPropes)(
ConfirmPropertyByUser
);
I am using redux and also updating the internal state. But I have heard somewhere that when using redux its unnecessary to update the internal state. How can i approach the following problem without updating the internal state? Can anyone help me on this, please?
What you're doing appears to be fine. Per the Redux FAQ, there's nothing wrong with using component state in a Redux app. For forms, it's very common to need to have both an "original" set of values and a "work-in-progress" copied set of values, and it's up to you whether the "WIP" values are stored in Redux or in a React component.
For what it's worth, I did show some examples of putting the "WIP" form state into Redux in my blog post Practical Redux, Part 8: Form Draft Data Management, which might be a useful reference. But, overall, your code here looks good - you're correctly copying props to state in the constructor and in componentWillReceiveProps, and the conceptual approach you're following is perfectly fine.
One small stylistic suggestion: I generally recommend that people use the object shorthand syntax for the mapDispatch argument. In your case, it would look like:
const actions = {loadMyInfo : getMyInfo, updateMyInfo : updateMyInfo};
// later
export default connect(mapState, actions)(ConfirmPropertyByUser);