I am trying to create an application that allows users to add patient records that contain name, phone and diagnosis. It gives them the ability to add new patients, edit current patients or entirely delete them.
I am rendering the name, phone, diagnosis, the edit-diganosis button and the EditDiagnosis component. The EditDiagnosis component renders a form in which the input fields are loaded already with the values entered by the user so that they can edit it.
The problem is that if I have more than 2 patient records and I click on the edit-diganosis button on the second record, it loads two instances of the EditDiagnosis component, one under the second record and that is intended and one under the first record and that is not what I want. I only want when I click on the edit-diagnosis button of a certain record, it only opens the form to edit this record, not all records.
I know I am doing something wrong but I cannot figure out how to associate each button with its own record.
The components I need help with are: PatientDiagnosis and EditDiagnosis
import React from 'react';
import Header from './Header';
import PatientDiagnosis from './PatientDiagnosis';
import AddPatient from './AddPatient';
import DiagnosisForm from './DiagnosisForm';
import EditDiagnosis from './EditDiagnosis';
export default class App extends React.Component {
state = {
patientsList: [],
inEditMode: false,
allowDelete: false
}
componentDidMount() {
try {
const data = localStorage.getItem('patientsList');
const parsedData = JSON.parse(data);
if (parsedData) {
this.setState(() => ({ patientsList: parsedData }));
}
} catch (e) {
}
}
componentDidUpdate(undefined, prevState) {
if (prevState.patientsList.length !== this.state.patientsList.length) {
const data = JSON.stringify(this.state.patientsList);
localStorage.setItem('patientsList', data);
}
}
addDiagnose = (patient) => {
if(patient.name && patient.phone && patient.diagnosis) {
this.setState((prevState) => ({ patientsList: prevState.patientsList.concat(patient)}));
}
else {
return 'Please fill out all the fields to continue.';
}
}
editDiagnose = (patient, id) => {
if (!patient.name || !patient.phone || !patient.diagnosis) {
return "Please fill out all the fields to continue.";
}
this.setState(prevState => {
prevState.patientsList[id] = patient;
return {
patientsList: prevState.patientsList,
inEditMode: !this.state.inEditMode
}
}
)
}
toggleEdit = (id) => {
console.log(id);
this.setState(() => ({inEditMode: !this.state.inEditMode}))
}
deleteDiagnose = (patientToDelete) => {
console.log(patientToDelete);
this.setState(
(prevState) => {
prevState.patientsList.map(patient => console.log(patient));
return {patientsList: prevState.patientsList.filter(patient => patientToDelete !==patient)};
}
)
}
render() {
return (
<div className="container">
<Header />
<PatientDiagnosis
patientsList={this.state.patientsList}
toggleEdit={this.toggleEdit}
inEditMode={this.state.inEditMode}
deleteDiagnose={this.deleteDiagnose}
editDiagnose={this.editDiagnose} />
<AddPatient addDiagnose={this.addDiagnose} />
</div>
)
}
}
const PatientDiagnosis = ({patientsList, deleteDiagnose, editDiagnose, inEditMode, toggleEdit}) => (
<div>
{patientsList.map((patient, index) => {
console.log(inEditMode);
return (
<div key={index}>
<h1>{patient.name}</h1>
<h1>{patient.phone}</h1>
<h1>{patient.diagnosis}</h1>
<button onClick={e => {e.preventDefault(); toggleEdit(patient)}}>Edit Diagnosis</button>
{inEditMode && <EditDiagnosis id={index} patient={patient} editDiagnose={editDiagnose} deleteDiagnose={deleteDiagnose}/>}
</div>
)
})}
</div>
)
export default PatientDiagnosis;
export default class EditDiagnosis extends React.Component {
state = {
id: this.props.id,
name: this.props.patient.name,
phone: this.props.patient.phone,
diagnosis: this.props.patient.diagnosis,
error: undefined
}
editDiagnose = (e, id) => {
e.preventDefault();
id = this.state.id;
const name = this.state.name;
const phone = this.state.phone;
const diagnosis = this.state.diagnosis;
const patient = {name, phone, diagnosis}
const error = this.props.editDiagnose(patient, id);
this.setState(()=>({error}));
if (!error) {
e.target.elements.name.value = '';
e.target.elements.phone.value = '';
e.target.elements.diagnosis.value = '';
}
}
toggleEdit = (e) => {
e.preventDefault();
this.setState(() => ({allowEdit: !this.state.allowEdit}))
}
changeName = (e) => {
const name = e.target.value;
this.setState(() => ({name}))
}
changePhone = (e) => {
const phone = e.target.value;
this.setState(() => ({phone}))
}
changeDiagnosis = (e) => {
const diagnosis = e.target.value;
this.setState(() => ({diagnosis}))
}
render() {
return (
<div>
{this.state.error && <p className="alert alert-danger" role="alert">{this.state.error}</p>}
{
<form
className="add-option"
id="patient-form"
onSubmit={this.editDiagnose}>
<DiagnosisForm
changeName={this.changeName}
changePhone={this.changePhone}
changeDiagnosis={this.changeDiagnosis}
name={this.state.name}
phone={this.state.phone}
diagnosis={this.state.diagnosis}
editDiagnose={this.editDiagnose}/>
<button className="btn btn-success" type="submit">Save Changes</button>
<button
className="btn btn-danger"
onClick={
e => {
e.preventDefault();
console.log(this.props.deleteDiagnose);
this.props.deleteDiagnose(this.props.patient);
}
}>Delete Diagnosis</button>
</form>}
</div>
)
}
}
export default class AddPatient extends React.Component {
state = {
error: undefined,
addNewPatient: false
}
addNewPatient = (e) => {
e.preventDefault();
this.setState(() => ({addNewPatient: !this.state.addNewPatient}));
}
cancelForm = (e) => {
e.preventDefault();
this.setState(() => ({error: undefined, addNewPatient: !this.state.addNewPatient}));
}
handleAddOption = (e) => {
e.preventDefault();
const name = e.target.elements.name.value.trim().toLowerCase();
const phone = e.target.elements.phone.value.trim().toLowerCase();
const diagnosis = e.target.elements.diagnosis.value.trim().toLowerCase();
const patient = {name, phone, diagnosis};
const error = this.props.addDiagnose(patient);
// this.setState(()=>({error, patientId: this.state.patientId + 1}));
if(patient.name && patient.phone && patient.diagnosis) {
this.setState(()=>({addNewPatient: !this.state.addNewPatient}));
}
if (!error) {
e.target.elements.name.value = '';
e.target.elements.phone.value = '';
e.target.elements.diagnosis.value = '';
}
}
render() {
return (
<div className="add-patient">
{this.state.error && <p className="alert alert-danger" role="alert">{this.state.error}</p>}
{!this.state.addNewPatient && <button onClick={this.addNewPatient} className="btn btn-primary">Add A New Patient</button>}
{this.state.addNewPatient &&
<form
className="add-option"
id="patient-form"
onSubmit={this.handleAddOption}>
<DiagnosisForm handleAddOption={this.handleAddOption}/>
<button className="btn btn-primary patient-form__btn" type="submit">Save</button>
<button onClick={this.cancelForm} className="btn btn-warning">Cancel</button>
</form>
}
</div>
)
}
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.6.0/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.6.0/umd/react-dom.production.min.js"></script>
Related
CodeSandbox
function disabledRow on click does not send changed data to child component.
I don't see any error in the code. Can't figure out how to display data in child component.
const UsersPage = () => {
const [dataState, setDataState] = useState<DataType[]>(dataInitial);
const [idState, setIdState] = useState<number[]>();
const disabledRow = () => {
if (idState) {
dataInitial.forEach((item) => {
if (idState.some((id) => id === item.id)) {
item.activeStatus = false;
item.date = <StopOutlined />;
item.status = "Not active";
}
});
return setDataState(dataState);
}
};
const idRow = (id: number[]) => {
return setIdState(id);
};
console.log("Hello", dataState);
return (
<div>
<div className={"wrapper"}>
<Button onClick={disabledRow}>
<StopOutlined /> Deactivate
</Button>
</div>
<UsersTable data={dataState} idRow={idRow} />
</div>
);
};
I want to be able to type into my input fields, and then have a button show up beside it upon typing that says submit edit. right now, I have a button that always is there, but I want it to only show up upon typing. this is all in react btw. so far, I have tried jquery, but react doesn't like it.
here's the whole page, to avoid any confusion of what I am doing and where my stuff is located.
import React, { Component } from "react";
import axios from "axios";
import "../styles/TourPage.css";
class TourPage extends Component {
constructor(props) {
super(props);
this.state = {
myData: [],
isLoading: true,
};
}
componentDidMount() {
axios
.get("/getResults")
.then((res) => {
this.setState({
myData: res.data
});
})
.catch((error) => {
// Handle the errors here
console.log(error);
})
.finally(() => {
this.setState({
isLoading: false
});
});
}
deleteById = (id) => {
console.log(id)
axios
.post(`/deleteDoc`, {id: id} )
.then(() => {
console.log(id, " worked")
window.location = "/tour"
})
.catch((error) => {
// Handle the errors here
console.log(error);
})
}
editById = (id, siteLocation, Services, cnum) => {
console.log(id, siteLocation, Services, cnum)
axios
.post(`/editDoc`, JSON.stringify({id: id, location: siteLocation, Services: Services, cnum: cnum}),{
headers: {
"Content-Type": "Application/json"
}
} )
.then(() => {
console.log(id, " worked")
window.location = "/tour"
})
.catch((error) => {
// Handle the errors here
console.log(error);
})
}
render() {
// You can handle the loader part here with isLoading flag. In this case No data found will be shown initially and then the actual data
let { myData, isLoading } = this.state;
return (
<table id="customers">
<tr>
<th>siteLocation</th>
<th>Services</th>
<th>cnum</th>
</tr>
{myData.length > 0
? myData.map(({ location, Services, cnum, _id }, index) => (
<tr key={index}>
<td><input type="text" placeholder={location} name="location" id="location" /> </td>
<td><input type="text" placeholder={Services} name="Services" id="Services" /> </td>
<td><input type="text" placeholder={cnum} name="cnumhide" id="cnumhide" /> </td>
<td><input type="hidden" placeholder={cnum} name="cnum" id="cnum" /> </td>
<button
onClick={(e) => {
e.preventDefault();
this.deleteById(_id);
}}
disabled={isLoading}
>
Delete
</button>
<button
onClick={(e) => {
e.preventDefault();
var siteLocation = document.getElementById('location').value
var Services = document.getElementById('Services').value
var cnum = document.getElementById('cnum').value
this.editById(_id, siteLocation, Services, cnum)
}}
>
Submit Edit
</button>
</tr>
))
: "No Data Found"}
</table>
);
}
}
const script = document. createElement("script"); $('input').keyup(function(){
if($.trim(this.value).length > 0)
$('#location').show()
else
$('#location').hide()
});
export default TourPage;
thanks 4 the help in advance.
You can use onfocus() in the text element. If you want to hide the button, use onfocusout() or in case if you want to track only after input has changed, use onchange() event
...
//class function
onTyping =()=>{
this.setState({
showSubmit:true
})
}
...
//render part
render(){
...
//input which you want to track typing
<input type="text" onfocus={()=>this.onTyping()} placeholder={location} name="location" id="location" />
...
//element submit button
{this.state.showSubmit && <button
onClick={(e) => {
e.preventDefault();
var siteLocation = document.getElementById('location').value
var Services = document.getElementById('Services').value
var cnum = document.getElementById('cnum').value
this.editById(_id, siteLocation, Services, cnum)
}}
>
Submit Edit
</button>}
...
Here is an example that helps you,
const {
useState
} = React;
const Test = () => {
const [show, setShow] = useState(false);
const handleChange = (event) => {
if (event.target.value.length > 0)
setShow(true);
else
setShow(false)
}
return ( <div>
<input type = "text"
onChange = {
(event) => handleChange(event)
}/>
{show && < button > Submit changes now! </button>}
</div>
)
}
ReactDOM.render( < Test / > ,
document.getElementById('root')
)
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/17.0.1/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/17.0.1/umd/react-dom.production.min.js"></script>
<div id="root"></div>
There is a way to avoid jquery and continue using your react class component to achieve this.
Map over state.myData to render each item with an input and a button.
Use the array index with the input's onChange event callback to add the inputValue into the correct array item's object within state.
Use the array index with the button's onClick event callback to get the item from state.myData before sending it to the server.
If there is an inputValue for the item, you can conditionally render the button.
import React, { Component } from "react";
import axios from "axios";
class TourPage extends Component {
constructor(props) {
super(props);
this.state = {
myData: [],
isLoading: true
};
}
componentDidMount() {
axios
.get("https://rickandmortyapi.com/api/character")
.then((res) => {
this.setState({
myData: res.data.results
});
})
.finally(() => {
this.setState({
isLoading: false
});
});
}
handleChangeInput = ({ target }, index) => {
const newData = [...this.state.myData];
newData[index].inputValue = target.value;
this.setState({
myData: newData
});
};
handleSubmitEdit = (index) => {
const item = this.state.myData[index];
// submit the edit to the api
console.log(
`Clicked on 'submit edit' for ${item.name} with value ${item.inputValue}`
);
};
render() {
let { myData, isLoading } = this.state;
if (isLoading) {
return "loading...";
}
return (
<div>
{myData.map(({ name, status, species, inputValue }, index) => {
return (
<div key={index}>
<p>{`${name} - ${species} - ${status}`}</p>
<input
type="text"
onChange={(e) => this.handleChangeInput(e, index)}
value={inputValue || ""}
/>
{inputValue && (
<button onClick={() => this.handleSubmitEdit(index)}>
Submit Edit
</button>
)}
</div>
);
})}
</div>
);
}
}
export default TourPage;
If you wanted to have an input per field within each row, you could make some small changes and save your edits to the item's state within a nested object.
Then you could check if there was anything inside that row's edits object to conditionally show the submit button per row.
import React, { Component } from "react";
import axios from "axios";
import isEmpty from "lodash.isempty";
import pick from "lodash.pick";
class TourPage extends Component {
constructor(props) {
super(props);
this.state = {
myData: [],
isLoading: true
};
}
componentDidMount() {
axios
.get("https://rickandmortyapi.com/api/character")
.then((res) => {
this.setState({
// here we create an empty 'edits' object for each row
myData: res.data.results.map((d) => ({
...pick(d, ["name", "status", "species"]),
edits: {}
}))
});
})
.finally(() => {
this.setState({
isLoading: false
});
});
}
handleChangeInput = ({ target }, index) => {
const newData = [...this.state.myData];
const { value, name } = target;
newData[index].edits[name] = value;
this.setState({
myData: newData
});
};
handleSubmitEdit = (index) => {
const item = this.state.myData[index];
// submit the edit to the api
console.log(`Clicked on 'submit edit' for ${item.name} with edits:`);
console.log(item.edits);
console.log("Updated item: ");
const { edits, ...orig } = item;
const newItem = { ...orig, ...edits };
console.log(newItem);
// Once saved to api, we can update myData with newItem
// and reset edits
const newData = [...this.state.myData];
newData[index] = { ...newItem, edits: {} };
this.setState({
myData: newData
});
};
showButton = (index) => {
const { edits } = this.state.myData[index];
return !isEmpty(edits);
};
render() {
let { myData, isLoading } = this.state;
if (isLoading) {
return "loading...";
}
return (
<table>
<tbody>
{myData.map((row, index) => {
const { edits, ...restRow } = row;
return (
<tr key={index}>
{Object.keys(restRow).map((col) => {
return (
<td>
<label>
{col}:
<input
name={col}
value={edits[col] || restRow[col]}
onChange={(e) => this.handleChangeInput(e, index)}
/>
</label>
</td>
);
})}
<td>
{this.showButton(index) && (
<button onClick={() => this.handleSubmitEdit(index)}>
Submit Edit
</button>
)}
</td>
</tr>
);
})}
</tbody>
</table>
);
}
}
export default TourPage;
I have created a component to function as a "Like/Unlike" button. When the state is true, the "Unlike" button successfully displays, but when I click "Unlike", and it DOES unlike successfully, the state should be set to false as (liked: false). However, I don't see the button.
One thing I noticed is, when I click "Unlike", the "Unlike" button disappears and the "Like" button does appear, for a millisecond, and then it vanishes in thin air. I cannot figure it out why.
Here are all the codes for my like button component:
import React from "react";
import { API, graphqlOperation } from "aws-amplify";
import { Button } from "element-react";
import { createLike, deleteLike } from "../graphql/mutations";
import { UserContext } from "../App";
class Like extends React.Component {
state = {
liked: "",
};
componentDidMount() {
this.setLiked();
}
setLiked() {
console.log(this.props);
const { user } = this.props;
const { post } = this.props;
if (post.likes.items.find((items) => items.liker === user.username)) {
this.setState({ liked: true });
console.log("liked: true");
} else {
this.setState({ liked: false });
console.log("liked: false");
}
}
handleLike = async (user) => {
try {
const input = {
liker: user.username,
likePostId: this.props.postId,
};
await API.graphql(graphqlOperation(createLike, { input }));
this.setState({
liked: true,
});
console.log("Liked!");
} catch (err) {
console.log("Failed to like", err);
}
};
handleUnlike = async (likeId) => {
try {
const input = {
id: likeId,
};
await API.graphql(graphqlOperation(deleteLike, { input }));
this.setState({
liked: false,
});
console.log("Unliked!");
} catch (err) {
console.log("Failed to unlike", err);
}
};
render() {
const { like } = this.props;
const { liked } = this.state;
return (
<UserContext.Consumer>
{({ user }) => (
<React.Fragment>
{liked ? (
<Button type="primary" onClick={() => this.handleUnlike(like.id)}>
Unlike
</Button>
) : (
<Button
type="primary"
onClick={() => this.handleLike(user, like.id)}
>
Like
</Button>
)}
</React.Fragment>
)}
</UserContext.Consumer>
);
}
}
export default Like;
The code of the parent component:
import React from "react";
import { API, graphqlOperation } from "aws-amplify";
import {
onCreateComment,
onCreateLike,
onDeleteLike,
} from "../graphql/subscriptions";
import { getPost } from "../graphql/queries";
import Comment from "../components/Comment";
import Like from "../components/Like";
import LikeButton from "../components/LikeButton";
import { Loading, Tabs, Icon } from "element-react";
import { Link } from "react-router-dom";
import { S3Image } from "aws-amplify-react";
import NewComment from "../components/NewComment";
class PostDetailPage extends React.Component {
state = {
post: null,
isLoading: true,
isAuthor: false,
};
componentDidMount() {
this.handleGetPost();
this.createCommentListener = API.graphql(
graphqlOperation(onCreateComment)
).subscribe({
next: (commentData) => {
const createdComment = commentData.value.data.onCreateComment;
const prevComments = this.state.post.comments.items.filter(
(item) => item.id !== createdComment.id
);
const updatedComments = [createdComment, ...prevComments];
const post = { ...this.state.post };
post.comments.items = updatedComments;
this.setState({ post });
},
});
this.createLikeListener = API.graphql(
graphqlOperation(onCreateLike)
).subscribe({
next: (likeData) => {
const createdLike = likeData.value.data.onCreateLike;
const prevLikes = this.state.post.likes.items.filter(
(item) => item.id !== createdLike.id
);
const updatedLikes = [createdLike, ...prevLikes];
const post = { ...this.state.post };
post.likes.items = updatedLikes;
this.setState({ post });
},
});
this.deleteLikeListener = API.graphql(
graphqlOperation(onDeleteLike)
).subscribe({
next: (likeData) => {
const deletedLike = likeData.value.data.onDeleteLike;
const updatedLikes = this.state.post.likes.items.filter(
(item) => item.id !== deletedLike.id
);
const post = { ...this.state.post };
post.likes.items = updatedLikes;
this.setState({ post });
},
});
}
componentWillUnmount() {
this.createCommentListener.unsubscribe();
}
handleGetPost = async () => {
const input = {
id: this.props.postId,
};
const result = await API.graphql(graphqlOperation(getPost, input));
console.log({ result });
this.setState({ post: result.data.getPost, isLoading: false }, () => {});
};
checkPostAuthor = () => {
const { user } = this.props;
const { post } = this.state;
if (user) {
this.setState({ isAuthor: user.username === post.author });
}
};
render() {
const { post, isLoading } = this.state;
return isLoading ? (
<Loading fullscreen={true} />
) : (
<React.Fragment>
{/*Back Button */}
<Link className="link" to="/">
Back to Home Page
</Link>
{/*Post MetaData*/}
<span className="items-center pt-2">
<h2 className="mb-mr">{post.title}</h2>
</span>
<span className="items-center pt-2">{post.content}</span>
<S3Image imgKey={post.file.key} />
<div className="items-center pt-2">
<span style={{ color: "var(--lightSquidInk)", paddingBottom: "1em" }}>
<Icon name="date" className="icon" />
{post.createdAt}
</span>
</div>
<div className="items-center pt-2">
{post.likes.items.map((like) => (
<Like
user={this.props.user}
like={like}
post={post}
postId={this.props.postId}
/>
))}
</div>
<div className="items-center pt-2">
{post.likes.items.length}people liked this.
</div>
<div>
Add Comment
<NewComment postId={this.props.postId} />
</div>
{/* Comments */}
Comments: ({post.comments.items.length})
<div className="comment-list">
{post.comments.items.map((comment) => (
<Comment comment={comment} />
))}
</div>
</React.Fragment>
);
}
}
export default PostDetailPage;
I think I know why it doesn't show up. It's because at first when the user hasn't liked it, there is no "like" object, so there is nothing to be shown, as it is only shown when there is a "like" mapped to it. I don't know how to fix it though.
I have a dynamically created list of 5 input elements. When I now click on a "plus" icon element (from IonicIcons) in React, I want the first of those input fields to be focused.
My input List:
if (actualState.showInputField === true) {
inputField = (
<div>
{
actualState.inputFields.map((val,index) => (
<ListElemErrorBoundary key={index}>
<InputElement key={index} elemValue = {val} name={"input" + index} onChangeListener={(event) => handleDoublettenIDs(event,index)} />
</ListElemErrorBoundary>
)
)
}
{actualState.errorMessage!= '' ? <Alert color="danger" >{actualState.errorMessage}</Alert> : null}
{actualState.successMessage !='' ? <Alert color="success" >{actualState.successMessage}</Alert> : null}
<br />
<p><button onClick = { () => {
handleInputs(props.anzeigeID);
vanishMessage();
}
}>absenden</button></p>
</div>
)
}
return inputField;
}
My Icon:
const checkIcon = () => {
let showIcon = null;
if (actualState.showInputField === false) {
showIcon = (
<IoIosAddCircleOutline ref={toggleSignRef} onClick = {toggleInput}
/>
)
} else {
showIcon = (
<IoIosRemoveCircleOutline onClick = {toggleInput}
/>
)
}
return showIcon;
}
I probably should place my ref on the list items, however, I guess that for every new list element, this ref gets "overwritten" because I have only one ref. Should I do something like a input key query, to find out which list key input element this is, and if it is the first input key index, I execute a focus on that input element?
And how then can I retrieve the first input element inside the method toggleInput() where I set the showInputField value? Is it somehow possible to ask for the props.key of that input element's reference?
This component is a functional component and I use useRef only...
My Component:
import React, {useState, useRef, useEffect} from "react";
import { IoIosAddCircleOutline } from 'react-icons/io';
import { IoIosRemoveCircleOutline } from 'react-icons/io';
import InputElement from './inputElementDublette';
import fetch from 'isomorphic-unfetch';
import getConfig from 'next/config';
import ListElemErrorBoundary from './ListElemErrorBoundary';
import { Button, Alert } from 'reactstrap';
let url_link;
let port = 7766;
const { serverRuntimeConfig, publicRuntimeConfig } = getConfig();
const apiUrl = publicRuntimeConfig.apiUrl; //|| publicRuntimeConfig.apiUrl;
const server = publicRuntimeConfig.SERVERNAME;
let doublettenListe_link = `http://${server}:${port}/doubletten/`;
//functional component with state, with useState
const DubletteComponent = props => {
const toggleSignRef = useRef();
const [actualState, changeState] = useState({
showInputField: false,
dublettenIDs: [],
errorMessage: '',
successMessage: '',
inputFields: ['','','','',''],
visible : false,
});
const toggleInput = () => {
changeState({...actualState, showInputField: !actualState.showInputField});
}
const vanishMessage = ()=>{
window.setTimeout(() => {
changeState({
...actualState,
errorMessage:'',
successMessage: '',
});
},7000);
}
const handleDoublettenIDs = (event,index) => {
let idnumber = event.target.value;
let newInputFields = [...actualState.inputFields];
newInputFields.splice(index,1, idnumber);
//console.log("new:",newInputFields);
if (isNaN(idnumber)) {
changeState({...actualState, errorMessage: 'ID is not a number'})
} if (idnumber > 2147483647) {
changeState({...actualState, errorMessage: 'Number can not be bigger than 2147483647!'})
}
else {
changeState({...actualState, inputFields: newInputFields, errorMessage: '' });
}
}
const handleInputs = (anzeigeID) => {
if (process.browser && apiUrl==='dev') {
doublettenListe_link = `http://localhost:${port}/doubletten/`;
}
if (actualState.errorMessage=='') {
let array = actualState.inputFields;
let filtered = array.filter(function(el) {
return el != '';
});
const requestOptions = {
method: 'POST',
headers: {'Accept': 'application/json', 'Content-Type':'application/json'},
body: JSON.stringify({
id: anzeigeID,
dublettenIDs: filtered
})
};
//console.log("inputfields:",filtered);
// Note: I'm using arrow functions inside the `.fetch()` method.
// This makes it so you don't have to bind component functions like `setState`
// to the component.
//console.log("link:", doublettenListe_link);
fetch(doublettenListe_link , requestOptions)
.then((response) => {
//console.log("Resp:", response);
let tempArray = ['','','','',''];
changeState({...actualState, inputFields: tempArray});
//console.log(actualState);
changeState({...actualState, dublettenIDs: []});
changeState({...actualState, successMessage: `Doubletten-IDs wurden erfolgreich eingetragen!`});
return response;
}).catch((error) => {
changeState({...actualState, errorMessage: `Error beim Eintrage der Dubletten. Bitte prüfen, ob der Server läuft. Error: ${error.statusText}`});
});
}
}
const checkIcon = () => {
let showIcon = null;
if (actualState.showInputField === false) {
showIcon = (
<IoIosAddCircleOutline onClick = {toggleInput}
/>
)
} else {
showIcon = (
<IoIosRemoveCircleOutline onClick = {toggleInput}
/>
)
}
return showIcon;
}
const checkPrerequisites = () => {
//let errorMessage = '';
let inputField = null;
// if (actualState.errorMessage != '') {
// errorMessage = (
// <Alert color="danger">{actualState.errorMessage}</Alert>
// )
// }
//Outsourcing check for variable and return JSX elements on conditionals
if (actualState.showInputField === true) {
inputField = (
<div>
{
actualState.inputFields.map((val,index) => (
<ListElemErrorBoundary key={index}>
<InputElement key={index} elemValue = {val} name={"input" + index} onChangeListener={(event) => handleDoublettenIDs(event,index)} />
</ListElemErrorBoundary>
)
)
}
{actualState.errorMessage!= '' ? <Alert color="danger" >{actualState.errorMessage}</Alert> : null}
{actualState.successMessage !='' ? <Alert color="success" >{actualState.successMessage}</Alert> : null}
<br />
<p><button onClick = { () => {
handleInputs(props.anzeigeID);
vanishMessage();
}
}>absenden</button></p>
</div>
)
}
return inputField;
}
return (
<div >
{checkIcon()} Dubletten eintragen
{checkPrerequisites()}
</div>
)
}
export default DubletteComponent
My InputElement Component:
const inputElement = (props) => (
<p>
<input
ref={props.ref}
value ={props.elemValue}
name={props.name}
type="number"
max="2147483647"
placeholder="Doubletten-ID"
onChange={props.onChangeListener}>
</input>
</p>
)
export default inputElement
The issue is you can not pass ref from your parent component to child component. In new version of react you can achieve this by using forwardRef api. Use it like below if you are in react #16 version.
import React from 'react'
const inputElement = React.forwardRef(props) => (
<p>
<input
ref={props.ref}
value ={props.elemValue}
name={props.name}
type="number"
max="2147483647"
placeholder="Doubletten-ID"
onChange={props.onChangeListener}>
</input>
</p>
)
//To focus textinput
this.inputref.focus();
Happy coding :)
it is my first React App
i want create simple typeahead(autocomplete)
i want when i click on searched list of item, this item will show in value of my Parent input
now my click doesnt work, working only search by name
it is my parent
`
import React, { Component } from 'react';
import logo from './logo.svg';
import './Search.css';
import Sugg from './Sugg';
class Search extends Component {
constructor(props) {
super(props);
this.onSearch = this.onSearch.bind(this);
this.handleClickedItem = this.handleClickedItem.bind(this);
this.onClick = this.onClick.bind(this);
this.state = {
companies: [],
searchedList: [],
value: ''
}
}
componentDidMount() {
this.fetchApi();
console.log(this.state.companies);
}
fetchApi = ()=> {
const url = 'https://autocomplete.clearbit.com/v1/companies/suggest?query={companyName}';
fetch(url)
.then( (response) => {
let myData = response.json()
return myData;
})
.then((value) => {
let companies = value.map((company, i) => {
this.setState({
companies: [...this.state.companies, company]
})
})
console.log(this.state.companies);
});
}
onSearch(arr){
// this.setState({companies: arr});
};
handleInputChange = () => {
console.log(this.search.value);
let searched = [];
this.state.companies.map((company, i) => {
console.log(company.name);
console.log(company.domain);
const tempName = company.name.toLowerCase();
const tempDomain = company.domain.toLowerCase();
if(tempName.includes(this.search.value.toLowerCase()) || tempDomain.includes(this.search.value.toLowerCase())) {
searched.push(company);
}
})
console.log(searched);
this.setState({
searchedList: searched
})
if(this.search.value == '') {
this.setState({
searchedList: []
})
}
}
handleClickedItem(data) {
console.log(data);
}
onClick = e => {
console.log(e.target.value)
this.setState({ value: e.target.value});
};
render() {
return (
<div className="Search">
<header className="Search-header">
<img src={logo} className="Search-logo" alt="logo" />
<h1 className="Search-title">Welcome to React</h1>
</header>
<form>
<input
placeholder="Search for..."
ref={input => this.search = input}
onChange={this.handleInputChange}
/>
<Sugg searchedList={this.state.searchedList} onClick={this.onClick.bind(this)} />
</form>
</div>
);
}
}
export default Search;
`
and here my child component
i dont know how call correctly click event
import React from 'react';
const Sugg = (props) => {
console.log(props);
const options = props.searchedList.map((company, i) => (
<div key={i} >
<p onClick={() => this.props.onClick(this.props)}>{company.name}</p>
</div>
))
console.log(options);
return <div >{options}</div>
}
export default Sugg;
please help me who knows how it works
thanks a lot
In the parent you could modify your code:
onClick = company => {
console.log('company', company);
this.setState({ value: company.name});
};
and you don't need to bind this because onClick is an arrow function
<Sugg searchedList={this.state.searchedList} onClick={this.onClick} />
and in the child component, you need to use props from the parameters, not from the this context:
<p onClick={() =>props.onClick(company)}>{company.name}</p>