I am new to react and I am trying to make a POST request using text field data, can anyone help me with how to store that input and make a request after a button is pressed.
I attempted to use useRef() which allowed me to obtain the data however I was not able to store it as a data object to then persist.
Currently my data persists, however it persists an empty object and the state is not being updated.
If anyone can help, I will really appreciate that.
Below is my App.js class
import React, { useState, useEffect, useRef, Component } from 'react';
import axios from "axios";
const api = axios.create({
baseURL: "http://localhost:8080/artists"
});
class App extends Component {
state = {
artists: [],
theArtistName: ""
}
constructor(props){
super(props);
this.getArtists()
}
//calling this method will allow artist array to be populated everytime an event occurs, e.g POST, PUT, DELETE
getArtists = async () =>{
let data = await api.get("/").then(({ data }) => data);
this.setState({artists: data}) //setting our artists to be the data we fetch
}
createArtist = async () =>{
let response = await api.post('/', {name: this.state.theArtistName})
console.log(response)
this.getArtists()
}
deleteArtist = async (id) =>{
let data = await api.delete('/${id}')
this.getArtists();
}
handleAddArtist = (event) =>{
event.preventDefault()
this.setState({
theArtistName: event.target.value
})
const data = this.state.theArtistName
console.log(data)
}
componentDidMount(){
this.createArtist()
}
render(){
// const {theArtistName} = this.state
return(
<>
<input type={Text} placeholder="Enter Artist Name" name="theArtistName"></input>
<button onClick={this.createArtist}>Add Artist</button>
{this.state.artists.map(artist => <h4 key={artist.id}>{artist.name}
<button onClick={() =>this.deleteArtist(artist.id)}>Delete artist</button></h4>)}
</>
)
}
}
export default App;
this.setState is an async function, it takes second argument as callback. This should solve your problem. i.e.
import React, { useState, useEffect, useRef, Component } from "react";
import axios from "axios";
const api = axios.create({
baseURL: "http://localhost:8080/artists",
});
class App extends Component {
constructor(props) {
super(props);
this.state = {
artists: [],
theArtistName: "",
};
}
//calling this method will allow artist array to be populated everytime an event occurs, e.g POST, PUT, DELETE
getArtists = async () => {
let data = await api.get("/").then(({ data }) => data);
this.setState({ artists: data }); //setting our artists to be the data we fetch
};
createArtist = async () => {
let response = await api.post("/", { name: this.state.theArtistName });
console.log(response);
this.getArtists();
};
deleteArtist = async (id) => {
let data = await api.delete("/${id}");
this.getArtists();
};
handleAddArtist = (event) => {
event.preventDefault();
this.setState(
{
theArtistName: event.target.value,
},
() => {
this.createArtist();
}
);
};
componentDidMount() {
this.getArtists();
}
render() {
// const {theArtistName} = this.state
return (
<>
<input
type={Text}
placeholder="Enter Artist Name"
name="theArtistName"
></input>
<button onClick={this.handleAddArtist}>Add Artist</button>
{this.state.artists.map((artist) => (
<h4 key={artist.id}>
{artist.name}
<button onClick={() => this.deleteArtist(artist.id)}>
Delete artist
</button>
</h4>
))}
</>
);
}
}
export default App;
Let me know if it helps.
because react update state asynchronously so when you are invoking handleAddArtist function which update state the event might be gone so you need to store the value from the event in variable like this :
handleAddArtist = (event) =>{
event.preventDefault()
const {value} = e.target
this.setState({
theArtistName: value
})
}
and to check state update there is a lifecycle method called componentDidUpdate for class component and useEffect for functional component.
[edit]:
call this.createArtist() in componentDidUpdate like this :
componentDidUpdate(prevProps,prevState){
if(prevState.theArtistName!==this.state.theArtistName)
this.createArtist()
}
so the createArtist will fire only when theArtistName state change.
First of all, useRef is a hook only meant for function components and not for class components. For using Refs in class components use React.createRef().
Usually, HTML input elements maintain their own state. The usual way to access the value of an input element from a React component that renders it is to control the input element's state via this component by adding an onChange listener and a value attribute to the input element:
class App extends Component{
constructor(props) {
super(props);
this.state = {artistName: ""};
this.handleArtistNameChange = this.handleArtistNameChange.bind(this);
}
handleArtistNameChange(event) {
this.setState({artistName: event.target.value});
}
render(){
return (
<input
type="text"
value={this.state.artistName}
onChange={this.handleArtistNameChange}
/>
);
}
}
Whenever the value of the input element changes the App component will rerender with the most up-to-date value of the input in its state.
Here is a working example:
You can read more on using form elements in React here.
Related
I am trying to fetch data from firebase. I am able to get the data and update the state, but state returns undefined after render in my React context Provider. I have tried to use some of the Life cycle method like componentWillMount or calling my fetchData function my the constructor function , since it get called before render, but none is working. Below is my code.
import React, { Component } from 'react';
import { dataDB, productDetail } from './data';
import { db } from './config/fbConfig'
import { TimerSharp } from '#material-ui/icons';
const ProductContext = React.createContext();
class ProductProvider extends Component {
constructor(props) {
super(props)
this.state = {
products: []
}
this.fetchData()
}
fetchData = () => {
db.collection("projects")
.get()
.then(querySnapshot => {
const data = querySnapshot.docs.map(doc => doc.data());
console.log(data); //successfully returns the data
// this.setState({ projects: data });
this.setState(() => {
return {
projects: data
}
})
console.log(this.state.products) // successfully returns the data and update the state
});
}
render() {
console.log(this.state.products) // returns empty arr and I need it to return the updated state with data
return (
<ProductContext.Provider value={{
...this.state
}}>
{this.props.children}
</ProductContext.Provider>
)
}
}
const ProductConsumer = ProductContext.Consumer;
export { ProductProvider, ProductConsumer };
The issue is this.state.products get called before calling data in firebase. Please how can I be able to get data after render.
In fetchData() you set the attribute this.state.projects but in render you log this.state.products
I'm trying to pass data from my database to a page in my react project. The database stores the user data and the data is called with validateCookie() function. I'm getting data from the validateCookie function but I can't seem to get the data out of the function to the main page so I can use it to update the user's state and calendar and return that to update their information in the database.
The setState is not sending data to the page state. I've tried so much but I'm still new to react so I'm a bit out of my league
import ScheduleSelector from 'react-schedule-selector'
import React, { Component } from 'react';
import Moment from 'moment';
import { Row, Col, Button } from 'react-bootstrap';
import API from '../../utils/API';
class Availability extends Component {
constructor(props) {
super(props);
this.state = {
user: [],
email: "",
calendar: [],
schedule: [],
}
// this.handleInputChange = this.handleInputChange.bind(this);
// this.handleSubmit = this.handleSubmit.bind(this);
}
componentDidMount() {
this.validateCookie();
console.log(this.state.user); // coming back empty because validate cookie is not passing data upstream
}
handleSubmit = (event) => {
event.preventDefault();
// let schedule = this.state.schedule;
// // alert("Your availability has been submitted successfully!");
// let ISOschedule = this.state.schedule.map(date => Moment(date).toISOString());
// let newCalendar = this.state.schedule
console.log(this.state.user);
API.updateAvailability(
this.state.user.email,
this.state.user.calendar,
this.state.user.schedule)
.then(r => {
console.log(r);
}).catch(e => {
console.log(e);
})
}
handleChange = newSchedule => {
this.setState({ schedule: newSchedule.map(date => Moment(date).toISOString()) })
}
validateCookie() {
API.validateCookie()
.then(res => res.json())
.then(res => {this.setState({ user: res})})
.then(res => {
console.log(this.state) // coming back with loading data aka empty
console.log(this.state.user) // coming back with all appropriate data
})
.catch(err => console.log(err));
console.log(this.state.user) // coming back empty
}
render() {
return (
<div>
<form ref="form" onSubmit={this.handleSubmit}>
<ScheduleSelector
selection={this.state.schedule}
numDays={7}
minTime={0}
maxTime={23}
onChange={this.handleChange}
/>
<Row>
<Col>
<Button type="submit" className="float-right">Submit Availability</Button>
</Col>
</Row>
</form>
</div>
)
}
}
export default Availability;
I think the problem is that in your validateCookie method, you are expecting the state to change as soon as you call the setState function. It is important to know that setState() does not immediately mutate this.state but creates a pending state transition.
Refer to this answer for more information.
One solution could be to check when this.state actually gets updated before you render anything in your render function.
Just like Swanky said, the setState() doesn't update immediately and you can listen for state change and re-render the UI. I have done some cleaning up to your setState below;
validateCookie = () => {
API.validateCookie()
.then(res => res.json())
.then(res => {
this.setState({...this.state, user: res.user})
console.log(this.state.user);
})
.catch(err => console.log(err));
}
I have two components: which takes value from an input field. Second component is which I fetch api data. The problem is that I want to get the value from GetSearch as the value i search the API in Pexels.
I have tried to change my code multiple times. I just cant understand how it is supposed to be done, and how should I actually communicate together with my components.
import React from "react";
class GetSearch extends React.Component {
constructor(props) {
super(props);
this.state = {value: ''};
this.handleChange = this.handleChange.bind(this);
this.handleSubmit = this.handleSubmit.bind(this);
}
handleChange(event) {
this.setState({value: event.target.value});
}
handleSubmit(event) {
alert('A name was submitted: ' + this.state.value);
event.preventDefault();
}
render() {
var PassValue = React.CreateClass({
render: function() {
return (
<p>{this.state.value}</p>
);
},
});
return (
<form className="search-form">
<input
type="text"
placeholder="Search for images"
value={this.state.value}
onChange={this.handleChange}/>
<input type="submit"/>
</form>
);
}
}
export default GetSearch
import React, { Component } from 'react'
export default class Pexels extends Component {
componentDidMount(){
let query = "water"
const url = `https://api.pexels.com/v1/search?query=${query}e+query&per_page=15&page=1`
const api_key = "xxxxxxxxxxxx"
fetch(url, {
method: 'GET',
headers: new Headers({
'Authorization': api_key
})
})
.then(response => response.json())
.then(data => {
console.log(data)
})
.catch(error => console.error(error))
}
render() {
return (
<h1>Hello</h1>)
}
}
So as you can see now: Pexels sends a get request with the value of water: let query = "water", which works fine. But I need the value from
this.state.value in the GetSearch component
First, you need to create a parent class. Then You need to pass callback functions to the children as props. Here GetSearch component can be your child class. After you click search button your main class method will notify that change. Then create your logic as you want.
Follow this example code. thanks
Parent Component
var ParentComponent = React.createClass({
update: function() {
console.log("updated!");
},
render: function() {
<ChildComponent callBack={this.update} />
}
})
Child Component
var ChildComponent = React.createClass({
preupdate: function() {
console.log("pre update done!");
},
render: function() {
<button onClick={this.props.callback}>click to update parent</button>
}
})
You may need a store(just a function) to fetch url data rather than in a UI component Pexels.
In GetSearch invoke the store function with input as parameter and return a promise, and get data in callback.
i'm having a table component for displaying some data. After dispatching an action the table data in the state are channging. However my table component is not updated. It is updated only when i click on another radio button in another row of my table. I want my component to rerender when the data are changed. Here is my code:
const mapStateToProps = state => ({
evaluationData: evaluationResultsSelector(state)
});
const mapDispatchToProps = dispatch => ({
setSelectedEvaluationRecord: record =>
dispatch(setSelectedEvaluationRecord(record))
});
export default connect(mapStateToProps,
mapDispatchToProps
EvaluationDataTable,
);
and my component is this:
import React from 'react';
import Table from 'antd/lib/table';
import 'antd/lib/table/style/css';
import "antd/dist/antd.css";
import { columnEvaluation } from './evaluationDataStructure';
class EvaluationDataTable extends React.Component {
constructor(props) {
super(props);
this.state = {
selectedRowKeys: [0], // Check here to configure the default column
};
}
// shouldComponentUpdate(prevProps, prevState) {
// return (prevProps.results !== this.props.results || prevState.selectedRowKeys !== this.state.selectedRowKeys);
// }
onRowChange = selectedRowKeys => {
if (selectedRowKeys.length > 1) {
const lastSelectedRowIndex = [...selectedRowKeys].pop();
this.setState({ selectedRowKeys: lastSelectedRowIndex });
}
this.setState({ selectedRowKeys });
};
onRowSelect = (record) => {
this.props.setSelectedEvaluationRecord(record)
};
render() {
const { selectedRowKeys } = this.state;
const rowSelection = {
type: 'radio',
selectedRowKeys,
onChange: this.onRowChange,
onSelect: this.onRowSelect
};
return (
<React.Fragment>
<div style={{ marginBottom: 16 }} />
<Table
rowSelection={rowSelection}
columns={columnEvaluation}
dataSource={this.props.evaluationData}
/>
</React.Fragment>
);
}
}
export default EvaluationDataTable;
When i click in another row the table is rerendered as my setState is triggered but when the data are channged the table is not rerendered. Only when i click in another row. How to deal with it? Thanks a lot
Also my reducer which mutates the table is this:
case ACTION_TYPES.EDIT_EVALUATION_RESULTS: {
const evaluationResults = state.evaluationResults;
const editedRecord = action.payload.editedEvaluationData;
evaluationResults.forEach((item, i) => {
if (item.id === editedRecord.id) {
evaluationResults[i] = editedRecord;
}
});
return {
...state,
evaluationResults
};
}
Problem was here as OP has already deduced.
const evaluationResults = state.evaluationResults;
This was causing a state-mutation which goes against Redux principles. Although the state values were being updated in OP's proceeding code, the changes were being made to the same, initial object in reference. Redux does not register it as a new-state so it found no need to re-render our component. To get your connected-component to re-render we need a completely new redux-state.
To achieve this, we need to create a brand-new copy of evaluationResults like so and then the OP's feature will work as expected:
const evaluationResults = [...state.evaluationResults];
I'm pulling data from my my database which needs to be available prior to the mounting of the component in order for the page to be populated with the componentDidMount() lifecycle method. I've verified that if i remove the setState and console.log my data, it does fetch from the DB as expected, but when I try to assign the data to my state variable, it return a error stating Unable to get property 'setState' of undefined or null reference within my componentWillMount() lifecycle method. I've listed my ReactJS code below.
import React, { Component, PropTypes } from 'react';
import Picture from '../../components/picture.jsx';
import { browserHistory } from 'react-router';
export default class Products extends Component {
constructor(props) {
super(props);
this.state = {clothingData: ''};
}
componentWillMount(){
fetch('/t')
.then(function(result){
return result.json();
})
.then(function(re){
this.setState({ clothingData: re });
console.log(this.state.clothingData);
})
.catch(function(error){
console.log(error);
});
}
componentDidMount(){
//empty for now
}
render(){
var MyArray = ['justin','tiffany','joe','john','karissa','pam','joseph','sean','kim'];
var imageSrc = ['http://placehold.it/249x373','http://placehold.it/249x373','http://placehold.it/249x373','http://placehold.it/249x373','http://placehold.it/249x373',
'http://placehold.it/249x373', 'http://placehold.it/249x373', 'http://placehold.it/249x373'];
return (
<div>
<Picture src = {imageSrc} onClick = { () => {browserHistory.push('/Product'); }} name = {MyArray} amount = {8} />
</div>
);
}
}
The problem is that this is being reassigned from the component instance to the function instance/global object.
componentWillMount() {
fetch('/t')
.then((result) => {
return result.json();
})
.then((re) => {
this.setState({ clothingData: re });
console.log(this.state.clothingData);
})
.catch(function(error){
console.log(error);
});
}
will work just fine since the arrow function will ensure that the this is bound to the component instance so this.setState will actually be defined. Whereas what you have the this is being set to the global object which does not have a property of setState