I'm a complete beginner. I have the following React component where I need to display a table with data and perform a search on it using a generated API using Laravel as backend.
Displaying data works great, searching data directly from the API link works, but when trying to perform the search on the table, all the data that was showing disappears and no result is displaying.
If it can help, in chrome's console when typing I have the following
SyntheticEvent {dispatchConfig: {…}, _targetInst: FiberNode, nativeEvent: InputEvent, type: "change", target: input.form-control, …}
SyntheticEvent {dispatchConfig: {…}, _targetInst: FiberNode, _dispatchInstances: FiberNode, nativeEvent: InputEvent, _dispatchListeners: ƒ, …}
I believe I have a problem in handleSearchChange
class Patient extends React.Component {
constructor(props) {
super(props)
this.state = {
patients : [],
};
this.handleSearchChange = this.handleSearchChange.bind(this);
}
// Problem in this one
handleSearchChange(key){
console.log(key);
fetch("api/patients/search?q="+key)
.then(response => {
this.setState({
patients:[]
});
})
}
componentDidMount() {
axios.get('api/patients')
.then(response => {
this.setState({
patients: response.data})
})
.catch(err => console.log(err));
}
render() {
return (
<div>
<Container>
<div>
<input
type="text"
className="form-control"
placeholder="Search..."
onChange= onChange={this.handleSearchChange}
/>
</div>
<Table>
<Thead>
<Tr>
<Th>ID</Th>
<Th>FIRST NAME</Th>
<Th>LAST NAME</Th>
</Tr>
</Thead>
<Tbody>
{this.state.patients.reverse().map((patient) => (
<Tr>
<Td>
{patient.id}
</Td>
<Td>
{patient.firstname}
</Td>
<Td>
{patient.lastname}
</Td>
</Tr>
))}
</Tbody>
</Table>
</Container>
</div>
);
}
}
export default Patient;
It's the problem with your input
<input
type="text"
className="form-control"
placeholder="Search..."
onChange= onChange={this.handleSearchChange}
/>
You need to capture onChange event from input, get the value then send it to handleSearchChange function, like this:
<input
type="text"
className="form-control"
placeholder="Search..."
onChange={e => this.handleSearchChange(e.target.value)}
/>
You might question why e.target.value, it's the event object, you can console.log it to see more detail.
Edit: in handleSearchChange function, you also need to setState the patients gotten from response,just like what you did in componentDidMount method
Related
i am working on project which contains more than 200 input fields for list.is it possible to manage them with single state input
import { useState } from "react";
import Item from "../Components/Item";
const initialState={
input:''
}
const List = () => {
const [values,setValues]=useState(initialState)
const handleChange=(e)=>{
setValues({
...values,[e.target.name]:e.target.value
})
}
return (
<div className="container">
<div className="listhead">
<h3 className="text-center">Price List-2022</h3>
<table className="table table-bordered border-dark table-hover text-center">
<thead className="bg-success text-white table-bordered border-dark">
<tr>
<th>S.No</th>
<th>Item</th>
<th>Price</th>
<th>Qty</th>
<th>Total</th>
</tr>
</thead>
<Item
product="bomb"
name="input"
price="50"
value={values.input}
handleChange={handleChange}
/>
<Item
product="chakkar"
name="input"
price="100"
value={values.input}
handleChange={handleChange}
/>
</table>
</div>
</div>
);
};
export default List;
child element
const Item = ({name,product,price,value,handleChange}) => {
return (
<tbody>
<tr>
<th>1</th>
<th>{product}</th>
<th>{price}</th>
<th className="w-25">
<input
name={name}
value={value}
onChange={handleChange}
type='number'
/>
</th>
<th> </th>
</tr>
</tbody>
);
};
export default Item;
with this code if i enter values in input fields all other input fields reads the same value. if i need to create 200 input field with data, what are the ways to do that?
You can pass all values to item
like this :
<Item
name="input"
price="50"
value={values}
handleChange={handleChange}
/>
and in Item component set props of input like this :
<input
name={name}
value={values[name]}
onChange={handleChange}
type='number'
/>
Your inputs have the same name, if you want to set the state for the different inputs you'll need a unique identifier.
This way you can use the e.target.name as a key in your state object.
<Item
product="Something"
name={"unique identifier"}
price="50"
value={values["unique identifier"]}
handleChange={handleChange}
/>
A good practice is also to use the callback function with the useState to make sure you have the most recent values.
setValues((prevValues) => {
return {
...prevValues,
[e.target.name]: e.target.value,
};
});
``
This question already has answers here:
Sort array of objects by string property value
(57 answers)
Closed 1 year ago.
Learning React thanks to your advices, I ended with this simple example, where everything runs nice & smooth. Question is: how can I implement a sort() method to order those data? ( I mean to show table Users ordering by LastName)....
PhoneBookForm:
import {useState} from 'react';
import InformationTable from './InformationTable';
export default function PhoneBookForm() {
const[user,setUser] = useState([]);
const [input,setInput]=useState({
firstName:'',
lastName:'',
phone:'',
});
function handleChange(e){
setInput({
...input,
[e.target.name]:e.target.value
})
}
function handleSubmit(e){
console.log(user)
e.preventDefault();
setUser(
[...user,{...input}])
setInput({
...input,
firstName:'',
lastName:'',
phone:''
})}
return (
<>
<form onSubmit={e => { e.preventDefault() }} style={style.form.container}>
<label>First name:</label>
<br />
<input
style={style.form.inputs}
className='userFirstname'
name='firstName'
type='text'
placeholder='Enter your name here...'
value={input.firstName}
onChange={handleChange}
/>
<br/>
<label>Last name:</label>
<br />
<input
style={style.form.inputs}
className='userLastname'
name='lastName'
type='text'
placeholder='Enter your Last Name here...'
value={input.lastName}
onChange={handleChange}
/>
<br />
<label>Phone:</label>
<br />
<input
style={style.form.inputs}
className='userPhone'
name='phone'
type='text'
placeholder='Enter your phone number...'
value={input.phone}
onChange={handleChange}
/>
<br/>
<input
style={style.form.submitBtn}
className='submitButton'
type='submit'
value='Add User'
onClick={handleSubmit}
/>
</form>
<InformationTable user={user}/>
</>
)
}
InformationTable :
export default function InformationTable({user}) {
// console.log(user);
return (
<div>
{user?.map((u)=>{
return(
<div key={u.phone}>
<table style={style.table} className='informationTable'>
<thead>
<tr>
<th style={style.tableCell}>{u.firstName}</th>
<th style={style.tableCell}>{u.lastName}</th>
<th style={style.tableCell}>{u.phone}</th>
</tr>
</thead>
</table>
</div>
)
})}
</div>
);
}
Currently, User data is showing, but with no order at all
Using state inside setState is React antipattern. SetState function has an callback with previous state, e.g.:
setState(previouState => previousState + 1)
Knowing this, you have two possibilities to sort your users. Directly in setUser function or directly when rendering.
Your handleSubmit function then could look like this
function handleSubmit(e){
console.log(user)
e.preventDefault();
setUser(prevUserState => {
newUsersArray = [...prevUserState, input]
newUsersArray.sort((a,b) =>
{ if (a.lastName > b.lastName) {
return 1
} else {
return -1
}
})
)
setInput({
firstName:'',
lastName:'',
phone:''
})}
If you want to sort the users till on rendering, then simply:
<div>
{user.sort((a,b) => {
if (a.lastName > b.lastName) return 1
else return -1
}).map((u)=>{
return(...
I'm learning React and I'm creating a little application of patient management. The project backend was coded in Laravel and working, now remains the frontend with React.
The display of the patients in a table using Axios is completely fine, but when I try to search, it seems to not work. The search code was pasted directly from a solution I stumbled on here with little adjustments.
For clarification, the API of the search when typed manually in the browser is working fine, so I have a problem with React.
How can I manage to search my table in React using my search API ?
Component JS:
import React, { Component } from "react";
import axios from "axios";
import { Link } from "react-router-dom";
import { Container, Dropdown, ListGroup, Button } from "react-bootstrap";
import { Table, Thead, Tbody, Tr, Th, Td } from "react-super-responsive-table";
import "./styles.css";
class Patient extends React.Component {
constructor(props) {
super(props);
this.state = {
patients: [],
};
}
//Pasted from a solution
const[patient,SearchData]=useState([]);
//End pasted
componentDidMount() {
axios
.get("api/patients")
.then((response) => {
this.setState({ patients: response.data });
})
.catch((err) => console.log(err));
// Pasted from a solution + This code seems to not work but the API is working
const Search=(key)=>{
console.log(key);
fetch("api/patients/search?q="+key)
.then((data)=>{
data.json().then((resp)=>{
console.warn("resp:",resp)
SearchData(resp)
})
})
}
//End pasted
}
render() {
return (
<div>
<Container>
<div className="col-md-4 my-2 my-md-0">
<div className="input-icon">
<input
type="text"
className="form-control"
placeholder="Search..."
onChange={(event) => Search(event.target.value)}
/>
<span>
<i className="flaticon2-search-1 text-muted" />
</span>
</div>
</div>
<Table className="table table-hover">
<Thead className="thead-light text-center">
<Tr>
<Th>ID</Th>
<Th>FIRST NAME</Th>
<Th>LAST NAME</Th>
</Tr>
</Thead>
<Tbody className="text-center">
{this.state.patients.reverse().map((patient) => (
<Tr>
<Td>
{patient.id}
</Td>
<Td>
{patient.firstname}
</Td>
<Td>
{patient.lastname}
</Td>
</Tr>
))}
</Tbody>
</Table>
</Container>
</div>
);
}
}
export default Patient;
It looks like you're defining Search inside ComponentDidMount, which will drop out of scope before the component renders. Try defining it as part of the component, instead.
You may also need to refer to it as this.Search inside the render block.
I am trying to hit an api available at covid19 api [ you can see directly , click it ] but I am not able to map through the state.
I tried browsing the questions and still didn't find it .
my code in app.js is
import React, { Component } from 'react'
import api from '../api/covidapi'
import SearchBar from './SearchBar'
class App extends Component {
constructor(props) {
super(props);
this.state = {
country : 'Please Enter the country',
active_cases : 'No country found',
countries : [],
errorMessage : '',
isLoading : false,
};
}
async componentDidMount() {
const response = await api.get('/summary');
console.log('data loaded = > ', response);
console.log(response.data.Countries.length) // giving 247
console.log('countries ', response.data.Countries) //lists all countries {....},{...}...
this.setState({countries:response.data.Countries})
// console.log('global end')
this.setState({
totalConfirmed : response.data.Global.TotalConfirmed,
})
} //component did mount ended.
onSearchSubmit = async (country) =>{
console.log(country)
try {
const response =
await api.get(`/live/country/${country}/status/confirmed`)
this.setState({country:country, active_cases:response.data[6].Active})
}
catch(e) {
this.setState({errorMessage : "Country Doesn't exist or misspelled"})
}
}; //onsearch submit ended.
render() {
return (
<div>
<div className="container">
<p style={{textAlign:'center',
backgroundColor:'green',
color:'white',
width:'97%',margin:'auto',
padding:'24px',
marginTop:'12px',}}>
Total confirmed as of now is <span> : </span>
<span style={{color : 'red'}} >
{this.state.totalConfirmed}
</span>
</p>
<SearchBar onSubmit = {this.onSearchSubmit}/>
</div>
<div className="container">
<h2 className="bg bg-primary" style={{marginBottom:'0px',
textAlign:'center',marginTop:'15px',
padding:'10px'}}>Covid19 Cases In single Country
</h2>
<table className="table table-striped">
<thead>
<tr>
<th>Country</th>
<th>Acitve Cases</th>
</tr>
</thead>
<tbody>
<tr>
<td>{this.state.country}</td>
<td>{ this.state.active_cases}</td>
</tr>
</tbody>
</table>
</div>
<br />
<hr />
<div className="container">
<div style={{textAlign:'center',color:'red',fontWeight:'bold'}}>
</div>
<h2 className="bg bg-primary" style={{marginBottom:'0px',
textAlign:'center', padding:'10px'}}>
Covid19 Cases Worldwide
</h2>
<table className="table table-striped table-hover table-dark">
<thead>
<tr>
<th>S.N</th>
<th>Country Name</th>
<th>Confirmed Cases</th>
<th> Total Deaths</th>
<th>Total Recovered</th>
</tr>
</thead>
<tbody>
{
Object.keys(this.state.countries).map((country) => (
<tr>
<td>{country}</td>
<td>........</td>
<td>........</td>
<td> ........</td>
<td>............</td>
</tr>
))
}
</tbody>
</table>
</div>
</div>
);
}
}
export default App;
and covidapi.js code is
import axios from 'axios'
export default axios.create({
baseURL :'https://api.covid19api.com'
})
The problem is in this section of the code in app.js
{
Object.keys(this.state.countries).map((country) => (
<tr>
<td>{country}</td>
<td>........</td>
<td>........</td>
<td> ........</td>
<td>............</td>
</tr>
))
}
I am not able to map through countries in my state , I think there is
problem with the Objects and array.
when Render the country that is passed in map it return the integer value like 1,2,3 .... and not able to get other values.
Response in the console looks like this
What I am trying is to display the all the countries list to the table whenever the application loads.
You can use this.state.countries directly without Object.keys() (It is an array already), and use the properties inside each item as follows:
{
this.state.contries.map(item => {
return(
<tr>
<td> { item.Country } </td>
<td> { item.totalConfirmed } </td>
... // Other tds
</tr>
)
}
}
I am trying to follow the Formik documentation on using FieldArrays so that I can add repeatable form elements to my form.
I've also seen this Medium post setting out an example.
I'm slow to learn and can't join the dots between the documentation and the implementation.
I want to have a button in my main form that says: "Add a request for data".
If that button is selected, then a nested form setting out the data profile is displayed, along with "add another data request" and "remove" buttons.
I have made the nested form in another component in my application, but I'm struggling to figure out how to use the example from the medium post to incorporate the nested form (as a repeatable element - ie someone might want 5 data requests).
Are there any examples of how to implement this?
In my code, I have basically followed the medium post, but tried to link the Data Request form component inside the index
<button
type="button"
onClick={() => arrayHelpers.insert(index, <DataRequestForm />)}>
Add a data request
</button>
This is plainly incorrect, but I can't get a handle on how to do this.
Taking Nithin's answer, I've tried to modify the embedded form so that I can use react-select, as follows, but I'm getting an error which says:
TypeError: Cannot read property 'values' of undefined
import React from "react";
import { Formik, Form, Field, FieldArray, ErrorMessage, withFormik } from "formik";
import Select from "react-select";
import {
Button,
Col,
FormControl,
FormGroup,
FormLabel,
InputGroup,
Table,
Row,
Container
} from "react-bootstrap";
const initialValues = {
dataType: "",
title: "",
description: '',
source: '',
}
class DataRequests extends React.Component {
render() {
const dataTypes = [
{ value: "primary", label: "Primary (raw) data sought" },
{ value: "secondary", label: "Secondary data sought"},
{ value: "either", label: "Either primary or secondary data sought"},
{ value: "both", label: "Both primary and secondary data sought"}
]
return(
<Formik
initialValues={initialValues}
render={({
form,
remove,
push,
errors,
status,
touched,
setFieldValue,
setFieldTouched,
handleSubmit,
isSubmitting,
dirty,
values
}) => {
return (
<div>
{form.values.dataRequests.map((_notneeded, index) => {
return (
<div key={index}>
<Table responsive>
<tbody>
<tr>
<td>
<div className="form-group">
<label htmlFor="dataRequestsTitle">Title</label>
<Field
name={`dataRequests.${index}.title`}
placeholder="Add a title"
className={"form-control"}
>
</Field>
</div>
</td>
</tr>
<tr>
<td>
<div className="form-group">
<label htmlFor="dataRequestsDescription">Description</label>
<Field
name={`dataRequests.${index}.description`}
component="textarea"
rows="10"
placeholder="Describe the data you're looking to use"
className={
"form-control"}
>
</Field>
</div>
</td>
</tr>
<tr>
<td>
<div className="form-group">
<label htmlFor="dataRequestsSource">Do you know who or what sort of entity may have this data?</label>
<Field
name={`dataRequests.${index}.source`}
component="textarea"
rows="10"
placeholder="Leave blank and skip ahead if you don't"
className={
"form-control"}
>
</Field>
</div>
</td>
</tr>
<tr>
<td>
<div className="form-group">
<label htmlFor="dataType">
Are you looking for primary (raw) data or secondary data?
</label>
<Select
key={`my_unique_select_keydataType`}
name={`dataRequests.${index}.source`}
className={
"react-select-container"
}
classNamePrefix="react-select"
value={values.dataTypes}
onChange={selectedOptions => {
// Setting field value - name of the field and values chosen.
setFieldValue("dataType", selectedOptions)}
}
onBlur={setFieldTouched}
options={dataTypes}
/>
</div>
</td>
</tr>
<tr>
<Button variant='outline-primary' size="sm" onClick={() => remove(index)}>
Remove
</Button>
</tr>
</tbody>
</Table>
</div>
);
})}
<Button
variant='primary' size="sm"
onClick={() => push({ requestField1: "", requestField2: "" })}
>
Add Data Request
</Button>
</div>
)
}
}
/>
);
};
};
export default DataRequests;
You cannot add nested forms inside a form element.
Please refer the below post for mode details.
Can you nest html forms?
If you are looking to nest multiple fields with a nested structure, inside a main form, you can achieve it using FieldArrays.
You can structure the form like.
{
firstName: "",
lastName: "",
dataRequests: []
}
Here firstName and lastName are top level form fields and dataRequests can be an array where each element follows the structure
{
requestField1: "",
requestField2: ""
}
Since dataRequests is an array, for rendering each item of FieldArray you need a map function.
form.values.dataRequests.map( render function() )
And for each rendered item, the change handlers should target their index to update the correct item in FieldArray.
<div key={index}>
<Field
name={`dataRequests.${index}.requestField1`}
placeholder="requestField1"
></Field>
<Field
name={`dataRequests.${index}.requestField2`}
placeholder="requestField2"
></Field>
<button type="button" onClick={() => remove(index)}>
Remove
</button>
</div>
In the above snippet name={dataRequests.${index}.requestField1} asks formik to update the key requestField1 of nth element of dataRequests array with the value of the input field.
Finally your <DataRequest /> component might look something like below.
import React from "react";
import { Field } from "formik";
export default ({ form, remove, push }) => {
return (
<div>
{form.values.dataRequests.map((_notneeded, index) => {
return (
<div key={index}>
<Field
name={`dataRequests.${index}.requestField1`}
placeholder="requestField1"
></Field>
<Field
name={`dataRequests.${index}.requestField2`}
placeholder="requestField2"
></Field>
<button type="button" onClick={() => remove(index)}>
Remove
</button>
</div>
);
})}
<button
type="button"
onClick={() => push({ requestField1: "", requestField2: "" })}
>
Add Data Request
</button>
</div>
);
};
And using <FieldArray /> you can connect <DataRequest /> to the main form.
You can try out the below sample SO snippet
function DataRequests({ form, remove, push }){
return (
<div>
{form.values.dataRequests.map((_notneeded, index) => {
return (
<div key={index}>
<Formik.Field
name={`dataRequests.${index}.requestField1`}
placeholder="requestField1"
></Formik.Field>
<Formik.Field
name={`dataRequests.${index}.requestField2`}
placeholder="requestField2"
></Formik.Field>
<button type="button" onClick={() => remove(index)}>
Remove
</button>
</div>
);
})}
<button
type="button"
onClick={() => push({ requestField1: "", requestField2: "" })}
>
Add Data Request
</button>
</div>
);
};
class Home extends React.Component {
initialValues = {
firstName: "",
lastName: "",
dataRequests: []
};
state = {};
render() {
return (
<div>
<Formik.Formik
initialValues={this.initialValues}
onSubmit={values => {
this.setState({ formData: values });
}}
>
{() => {
return (
<Formik.Form>
<div>
<Formik.Field
name="firstName"
placeholder="First Name"
></Formik.Field>
</div>
<div>
<Formik.Field
name="lastName"
placeholder="Last Name"
></Formik.Field>
</div>
<Formik.FieldArray name="dataRequests" component={DataRequests} />
<button type="submit">Submit</button>
</Formik.Form>
);
}}
</Formik.Formik>
{this.state.formData ? (
<code>{JSON.stringify(this.state.formData, null, 4)}</code>
) : null}
</div>
);
}
}
ReactDOM.render(<Home />, document.getElementById("root"))
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.6.3/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.6.3/umd/react-dom.production.min.js"></script>
<script src="https://unpkg.com/formik/dist/formik.umd.production.js"></script>
<div id="root"></div>
For anyone looking to learn from this post, the answer to this question from nithin is clearly motivated by good intentions, but it isn't a correct deployment of formik. You can check out a code sandbox here: https://codesandbox.io/embed/goofy-glade-lx65p?fontsize=14 for the current attempt at solving this problem (still not fixed) but a better step toward a solution. Thanks just the same for the helpful intentions behind the answer to this question.