React: [Label]-[input]-[edit btn]-[save btn-(hidden)] - javascript

As the title states, Ive got a simple crud operation I could use some help with the useEffect/useState hook in how to implement.
I've got the input set to disabled by default, displaying the previous value via the placeholder. The state is then updated via a useState/useEffect hooks. Also, the second button (save) is set to hidden by default.
Goal:
essentially, just trying to setup an event listener for each edit-input button: hide the edit button, enable the input, unhide the save button.
with a separate (2nd) event listener on the save button: to hide the save button, unhide the edit button, return the input to disabled, turn the placeholder to the new value, and submit the value(I've got a good idea of the last part)
JSX:
<label className="col">
<div className="row">
<p className="bold nowrap inline-value-title" >Test Name: </p>
<input
id="display-name-change"
type="text"
className="form-reset inline-input"
onChange={(e) => setDisplayName(e.target.value)}
value={displayName}
placeholder={user.displayName}
disabled
/>
<button type="button" className="inline-button edit-val-btn">
<FontAwesomeIcon icon={faPenToSquare} />
</button>
<button type="button" className="hidden inline-button save-val-btn">.
<FontAwesomeIcon icon={faCircleCheck} />
</button>
</div>
</label>
My Javascript: (as you can probably tell it's still very Vanilla and I think that's the problem)...
const editValueBtns = document.querySelectorAll('.edit-val-btn');
const saveValueBtns = document.querySelectorAll('.save-val-btn');
useEffect(() => {
editValueBtns.forEach((button) => {
button.addEventListener('click', (e) => {
button.classList.add('hidden')
button.nextSibling.classList.remove('hidden')
button.parentElement.children[1].removeAttr("disabled") ;
})
})
saveValueBtns.forEach((button) => {
button.addEventListener('click', (e) => {
button.classList.add('hidden')
button.previousSibling.classList.remove('hidden')
button.parentElement.children[1].addAttr("disabled") ;
})
})
}, []);
EDIT: Showing the inputs being submitted to Firebase/Firestore
const handleSubmit = async (e) => {
e.preventDefault();
let selectedFile = document.querySelector('#thumbnailInput')
// displayname
if(displayName.length == 0){console.log('No change of name')}
else {
console.log('change to displayname')
updateProfile(user, { displayName })
setDoc(doc(db, 'users', user.uid), { displayName }, { merge: true })
}
// phone Number
if (phoneNo.length == 0){console.log('No change of phone no')}
else {
console.log('change to phone')
updateProfile(user, { phoneNo })
setDoc(doc(db, 'users', user.uid), { phoneNo }, { merge: true })
}
// title
if (title.length == 0){console.log('No change of title')}
else {
console.log('change to title')
updateProfile(user, { title })
setDoc(doc(db, 'users', user.uid), { title }, { merge: true })
}
// avatar thumbnail
if(selectedFile.files[0] == undefined){
console.log('no change to thumbnail')
} else {
console.log('change to thumbnail')
// pass the path in ref to create a StorageReference
const storageRef = ref(storage,`thumbnails/${user.uid}/${displayName}`) //thumbnail.name
// upload image, file is a blob here
await uploadBytes(storageRef, thumbnail);
const downloadUrl = await getDownloadURL(storageRef);
// this function returns promise too, add await
await updateProfile(user, { photoURL: downloadUrl })
updateProfile(user, { photoURL: downloadUrl})
setDoc(doc(db, 'users', user.uid), {
photoURL: downloadUrl,
}, { merge: true })
}
// clear all form inputs
const inputs = e.target.querySelectorAll('.form-reset')
inputs.forEach((input) => {
input.value=""
})
}

I see your vanilla js way, and raise you the react way. In react, you shouldn't have to use document.querySelector, previousSibling, parentElement, classList.add, classList.remove, addAttr or button.addEventListener. See solution in CodeSandbox or below:
App.jsx
import { Row } from "./components/Row";
import "./styles.css";
export default function App() {
return (
<div className="App">
<Row placeholder="input 1" />
<Row placeholder="input 2" />
<Row placeholder="input 3" />
</div>
);
}
Row.jsx
import { useState } from "react";
export const Row = ({ defaultValue, placeholder }) => {
const [value, setValue] = useState(defaultValue);
const [disabled, setDisabled] = useState(true);
const handleEditClick = () => {
setDisabled(false);
};
const handleSaveClick = () => {
setDisabled(true);
// save logic goes here
};
return (
<label className="col">
<div className="row">
<p className="bold nowrap inline-value-title">Test Name:</p>
<input
type="text"
className="form-reset inline-input"
onChange={(e) => {
setValue(e.target.value);
}}
value={value}
placeholder={placeholder}
disabled={disabled}
/>
{disabled && (
<button
type="button"
onClick={handleEditClick}
className="inline-button edit-val-btn"
>
edit
</button>
)}
{!disabled && (
<button
type="button"
onClick={handleSaveClick}
className="hidden inline-button save-val-btn"
>
save
</button>
)}
</div>
</label>
);
};

small tweaks.. it appears that logging the console kept it from doing what it did. also removed the e from the parenthesis after each 'click'. the addAttr and removeAttr also were replaced... the rest of the functionality can be placed in either of the listeners..
EDIT: added a 2nd input and it appears to only work in the 1st input....
...yes I'm talking to myself.
EDIT 2: It worked fine until the page refreshed... removed the dependency array for it to work every time. I feel like i still need a cleanup function, but I can't just place the event listeners into a function can i? Really, if you're reading this i would love some more experienced input... :)
useEffect(() => {
editValueBtns.forEach((button) => {
button.addEventListener('click', () => {
button.classList.add('hidden')
button.nextSibling.classList.remove('hidden')
button.parentElement.children[1].disabled = false ;
})
})
saveValueBtns.forEach((button) => {
button.addEventListener('click', () => {
button.classList.add('hidden')
button.previousSibling.classList.remove('hidden')
button.parentElement.children[1].disabled = true ;
})
})
});

Related

Why isn't my child component updating data when changing the state in React?

I have a list of users and I want to display in another component on the same page the user data in input fields for every user that I click on.
When no user is selected, I want the component to just render some text and a button to add a user. When the button is clicked the component renders the form with empty input fields so that we can add a new user.
I tried the following, but it's just showing the data for the first one I click on. It's not updating.
The main page:
const index = (props) => {
const [selectedUser, setSelectedUser] = useState(null);
const [state, setState] = useState("Index");
const onChange = (item) => {
setState("Edit");
setSelectedUser(item);
};
const onClick = (e, item) => {
if (e.type === "click" && e.clientX !== 0 && e.clientY !== 0) {
onChange(item);
} else {
console.log('prevented "onClick" on keypress');
}
};
const renderComponent = () => {
switch (state) {
case "Index":
return (
<>
<div className="btn" onClick={(e) => setState("Edit")}>
+ New Staff
</div>
<img src="/storage/illustrations/collaboration.svg" />
</>
);
case "Edit":
return (
<div>
<StaffForm profile={selectedUser} />
</div>
);
}
};
return (
<>
<div>
<div>
<h1>Staff</h1>
</div>
<div>
<div>
{profiles.map((item, index) => {
return (
<div key={index} onClick={(e) => onClick(e, item)}>
<input
type={"radio"}
name={"staff"}
checked={state === item}
onChange={(e) => onChange(item)}
/>
<span>{item.user.name}</span>
</div>
);
})}
</div>
<div>{renderComponent()}</div>
</div>
</div>
</>
);
};
The Staff Form Component:
const StaffForm = ({ profile }) => {
const { data, setData, post, processing, errors, reset } = useForm({
email: profile ? profile.user.email : "",
name: profile ? profile.user.name : "",
phone_number: profile ? profile.user.phone_number : "",
avatar: profile ? profile.user.avatar : "",
});
const [file, setFile] = useState(data.avatar);
const handleImageUpload = (e) => {
setFile(URL.createObjectURL(e.target.files[0]));
setData("avatar", e.target.files[0]);
};
const onHandleChange = (event) => {
setData(
event.target.name,
event.target.type === "checkbox"
? event.target.checked
: event.target.value
);
};
return (
<div>
<ImageUpload
name={data.name}
file={file}
handleImageUpload={handleImageUpload}
/>
<TextInput
type="text"
name="name"
value={data.name}
autoComplete="name"
isFocused={true}
onChange={onHandleChange}
placeholder={t("Name")}
required
/>
<TextInput
type="text"
name="phone_number"
value={data.phone_number}
autoComplete="phone_number"
placeholder={t("Phone Number")}
onChange={onHandleChange}
required
/>
<TextInput
type="email"
name="email"
value={data.email}
autoComplete="email"
onChange={onHandleChange}
placeholder={t("Email")}
required
/>
</div>
);
};
First of all something you should avoid is the renderComponent() call.Check here the first mistake mentioned in this video. This will most likely fix your problem but even if it doesn't the video explains why it should not be used.
Something else that caught my eye(possibly unrelated to your question but good to know) is the onChange function. When two pieces of state change together it is a potential source of problems, check out this article on when to use the useReducer hook.
Also be careful with naming React Components, they need to be capital case, this question contains appropriate answers explaining it.
(To only solve your problem stick to no.1 of this list, there are some improvements i'd do here overall for code quality and beauty, msg me for more details)

Handling the Check Box filter in React Js

Im Having a Table which has multiple records and Filter component with the Search Bar. What im trying to do is Based on the value selected by the user from all the filters i have pass those arrays to parent and form an object,
Im having 3 components here,
1)Parent : Data
export default function Data(props) {
const [domain, setDomain] = useState([]);
const [fileType, setFileType] = useState([]);
const [entity, setEntity] = useState(["Patents"]);
const [year, setYear] = useState({});
//This is the search bar state
const [keywords, setKeywords] = useState([]);
//based on the filter values im calling the API to get the records for table based on the value selected by the user from my filer
useEffect(() => {
const fetchResults = async (projectid) => {
const url = props.apiURL.rpaapiurl + "/search";
console.log("fetchData called-->" + url);
const resultsObj = {
projectId: projectid,
filter: {
domain: domain,
fileType: fileType,
entity: entity,
},
};
const response = await fetch(url, {
method: "POST",
body: JSON.stringify(resultsObj),
headers: {
"Content-Type": "application/json",
},
});
const data = await response.json();
console.log("All data-->", data);
setResults(data);
};
fetchResults(5);
}, [domain, fileType, entity]);
const handleFileType = (fileTypeArray) => {
setFileType(fileTypeArray);
};
return (
<Item1>
<Dropdown onChangeFileType={(FileTypeFilteredArray) => handleFileType(FileTypeFilteredArray)} ></Dropdown>
</Item1>
<Item2>
<Table
Data={dataresults}
Attributes={resultTable}
entitytypeHandler={props.entitytypeHandler}
></Table>
</Item2>
)
From the data parent component im passing the hadler which will return updated array from the child and im setting it to state.
2)Child : Dropdown
export default function Dropdown(props) {
return (
<FilterItem>
<Input
type="search"
placeholder="Search in title, description, keywords"
></Input>
<Filter1></Filter1>
<Filetr2></Filetr2>
<ContentFormat
onChangeFileType={props.onChangeFileType}
></ContentFormat>
<Filter4></Filter4>
<Filter5></Filter5>
<TextWrap>
<P text="End year" fontSize="14px" color="#454545"></P>
<KeywordImg src={droparrow} />
</TextWrap>
</FilterItem>
)}
Nothing special here since we can not skip a component passing the same thing to nested child,
Nested Child : ContentFormat
export default function ContentFormat(props) {
const [isDisplay, setIsDisplay] = useState("false");
const array = ["HTML", "PDF"];
const toggle = () => {
setIsDisplay(!isDisplay);
};
let fileTypeArray = [];
const handleSelection = (event) => {
const value = event.target.value;
console.log("value-->", +value);
if (event.target.checked == true) {
fileTypeArray.push(value);
console.log("if fileTypeArray-->", fileTypeArray);
} else if (fileTypeArray.length > 0) {
fileTypeArray = fileTypeArray.filter((element) => {
console.log("element-->", +element);
if (event.target.value !== element) return element;
});
console.log("else fileTypeArray-->", fileTypeArray);
}
console.log("function fileTypeArray-->", fileTypeArray);
};
const applyClickHandler = () => {
console.log("Applied fileTypeArray-->", fileTypeArray);
props.onChangeFileType(fileTypeArray);
};
return (
<div>
<DropContent>
<DropButton onClick={toggle}>
{" "}
<P text="By Content Format" fontSize="14px" color="#454545"></P>
<KeywordImg src={droparrow} />
</DropButton>
<ContextWrapper style={{ display: isDisplay ? "none" : "block" }}>
<P
text="Filter by Extension types"
fontSize="18px"
color="#ACACAC"
textAlign="center"
padding="22px 32px 14px"
></P>
<DropScroll className="sl-style-3">
{array.map((item, index) => {
return (
<ContextItem key={index}>
<DropList
onHandleSelection={handleSelection}
text={item}
value={item}
></DropList>
</ContextItem>
);
})}
</DropScroll>
<ApplyButton onClick={applyClickHandler}>
<P text="Apply" fontSize="16px" color="#fff" textAlign="center"></P>
</ApplyButton>
</ContextWrapper>
</DropContent>
</div>
);
}
4)DropList
export default function DropList(props) {
const changeHandler = (e) => {
console.log(e);
props.onHandleSelection(e);
};
return (
<div>
<div className="">
<TickBox
type="checkbox"
id={props.id}
name={props.name}
value={props.value}
onChange={(e) => {
changeHandler(e);
}}
/>
{props.text}
</div>
</div>
);
}
I'm getting the updated array on click of apply button in the parent but if user un-selects any check box the it deleting the complete array
In data i have to form the object base on the state array passed by all the filters, i tried for the one filter as above but its not working can any one suggest better way to do it,
Because here handling one filter is default and i have to do it for total 5 filters
So any suggestion or one common component for all the filters
Im not sure whether i should be asking these kinda questions or not since I'm very at posting the right questios but pardon me if its wrong question or the way of asking is wrong,
Any help would be appricited.

Filtering data in a list (delete button) isn't working?

So I'm doing a list in which you can add items. When you add them you have two options:
Delete the whole list
Delete a specific item.
But for some reason the "handeDelete" button is not working. Can somebody tell me what did I write wrong in the code?
The link to CodeSandbox is:
codesandbox
import { useState } from "react";
import uuid from "react-uuid";
export default function ItemList() {
const [items, setItems] = useState({ item: "" });
const [groceryList, setGroceryList] = useState([]);
function handleChange(value, type) {
setItems((prev) => {
return { ...prev, [type]: value };
});
}
function handleSubmit(e) {
e.preventDefault();
const newItem = { ...items, id: uuid() };
setGroceryList([...groceryList, newItem]);
setItems({ item: "" });
}
function handleDelete(id) {
setGroceryList(groceryList.filter((items) => items.id !== id));
}
return (
<>
<form autoComplete="off" onSubmit={handleSubmit}>
<input
type="text"
name="item"
id="item"
value={items.item}
onChange={(e) => handleChange(e.target.value, "item")}
/>
</form>
{groceryList.map((list) => {
return (
<div key={list.id}>
<ul>
<li> {list.item}</li>
</ul>
<button onClick={(id) => handleDelete()}>Delete</button>
</div>
);
})}
<button onClick={() => setGroceryList([])}>Clear</button>
</>
);
}
Your delete button definition is wrong:
<button onClick={() => handleDelete(list.id)}>Delete</button>
the parameter you are receiving from the click event is not the id. Since you are not working with the event args itselfy you can safely ignore it. The second mistake was, that you are not passing the id itself to your handleDelete function.
For learning purposes, humor yourself and print the event to the console, while developing:
<button onClick={(evt) => {
console.log(evt)
handleDelete(list.id)
}}>
Delete
</button>
This will show you, that the parameter, that you named id (and I renamend to evt), is in fact reacts Synthetic Event: https://reactjs.org/docs/events.html

How do I sync tests in React?

I have CommentsList component which displays a list with all the comments. Each comment has a 'Reply to' button that opens the AddComment component (this component allows me to add a reply to a comment). To display the AddComment component for each comment, I used an array of states.
The AddComment component contains a text area for the input, a cancel button and a submit button. When I click on the submit button and the reply is added successfully, the AddComment component closes. If the input is empty and I click on the submit button, the component doesn't close because the input can't be empty in order to be submitted successfully.
I want to test this functionality so that I can verify if the AddComment component disappears after I post a reply.
The problem is that in tests my AddComment component doesn't disappear when I click on the submit button. What I noticed is that the comment is added succesfully, but the state of the AddComment component for the comment isn't changed. When I click on submit button the input is submitted, but the function that changes the state is never called. I think the problem might be the fact that the actions don't synchronize.
I tried to use await act to render CommentsList component to make sure that the test run closer to how React works in the browser, but my AddComment component still doesn't disappear.
Here's my CommentsList component
function CommentsList(props) {
const { t } = useTranslation();
const [hasReplyCommentBox, setHasReplyCommentBox] = useState([]);
function toggleHasReplyComment(commentIndex) {
var auxState = { ...hasReplyCommentBox };
auxState[commentIndex] = auxState[commentIndex] ? 0 : 1;
setHasReplyCommentBox(auxState);
}
function replyToCommentButton(commentIndex) {
return [
<span
id={"replyButton-" + commentIndex}
onClick={() => toggleHasReplyComment(commentIndex)}>
{t('Reply to')}
</span>
];
}
function commentReplyBox(commentIndex, parentCommentId) {
return hasReplyCommentBox[commentIndex]
?
<AddComment
id={props.codeId}
parentCommentId={parentCommentId}
commentIndex={commentIndex}
toggleHasReplyComment={toggleHasReplyComment}
updateComments={props.updateComments}
/>
:
null
}
return (
<Layout>
<Layout>
<List
itemLayout="horizontal"
dataSource={props.comments}
renderItem={(comment, commentIndex) => (
<List.Item>
<CommentCard
userId={comment.user_id}
datePosted={comment.date_posted}
body={comment.body}
actions={replyToCommentButton(commentIndex)}
children={commentReplyBox(commentIndex, comment.id)}
/>
</List.Item>
)}
/>
<AddComment
id={props.codeId}
updateComments={props.updateComments}
/>
</Layout>
</Layout>
);
}
Here's my AddComment component
function AddComment(props) {
const { t } = useTranslation();
const { TextArea } = Input;
const [form] = Form.useForm();
const [comment, setComment] = useState();
const [onCommentAddSuccess, setOnCommentAddSuccess] = useState(0);
const buttonStyle = {
float: 'right'
};
function onCommentChange(newComment) {
setComment(newComment.target.value);
}
function updateOnCommentAddSuccess(onCommentAddSuccess) {
setOnCommentAddSuccess(onCommentAddSuccess + 1);
}
function resetCommentInput() {
setComment('');
}
function onFormReset() {
form.resetFields();
}
function toggleHasReplyCommentOnPost(parentCommentId, commentIndex) {
if (parentCommentId !== undefined) {
console.log('comentariu adaugat cu succes');
props.toggleHasReplyComment(commentIndex);
}
}
function submitComment() {
let request = {
body: comment,
code_id: props.id,
parent_comment_id: props.parentCommentId
};
fetch('/api/comment/add',
{
method: 'POST',
body: JSON.stringify(request)
}
).then(response => response.json())
.then(data => {
if (data.success === 1) {
updateOnCommentAddSuccess(onCommentAddSuccess);
props.updateComments(onCommentAddSuccess);
resetCommentInput();
toggleHasReplyCommentOnPost(props.parentCommentId, props.commentIndex);
}
});
}
return (
<>
<Form form={form} name="comment" className="comment-form"
onFinish={submitComment}>
<Form.Item name="body" label={t('Comment')}>
<TextArea placeholder={t('Leave a comment')}
onChange={onCommentChange}
id={"parent-comment-" + props.parentCommentId} />
</Form.Item>
<Form.Item style={buttonStyle}>
<Space>
{props.parentCommentId
?
<Button id={"cancelAddReplyComment-" + props.parentCommentId}
type="secondary" className = "comment-form-button"
onClick={
() => props.toggleHasReplyComment(props.commentIndex)
}>
{t('Cancel')}
</Button>
:
null
}
<Button type="primary" htmlType="submit"
className = "comment-form-button"
id={"post-comment-button-" + props.parentCommentId}
onClick={onFormReset}>
{t('Post')}
</Button>
</Space>
</Form.Item>
</Form>
</>
);
}
And here's how my test looks like
test ('Toggle displaying add reply to comments', async () => {
const comments = [
{
id: 'ID-1',
user_id: 'USER-ID-1',
date_posted: '2020-01-01 01:00:00',
body: 'First comment'
}
];
await act(async () => {
Promise.resolve(render(
<CommentsList comments={comments} />, container
));
});
// Open AddComment component
const replyButton = container.querySelector("#replyButton-0");
await fireEvent.click(replyButton);
// Insert input in the text area
const userInput = container.querySelector("#parent-comment-ID-1");
await userEvent.type((userInput), 'reply');
// Submit the input
const postButton = container.querySelector("#post-comment-button-ID-1");
await fireEvent.click(postButton);
// Check if the AddComment component is closed
expect(container.querySelector("#cancelAddReplyComment-ID-1")).toBeFalsy();
});

How to clear input after form submit (React)

I have a search input I'd like to clear after the value is submitted and the search is performed with the value. In similar questions, it was suggested to set the state of the input value to '', but I think that's what I tried and it didn't do anything.
I only have a parent and child component in my app. The parent component has a method for searching jokes (searchJokes), and it is passed down as a prop with a different name to the child component in the component instance with onFormSubmit={this.searchJokes}. In the child component, when the user enters something into the search input, its event.target.value is passed with onChange={e => props.onInputChange(e.target.value)} corresponding to the onSearchChange method in the parent prop, and the value is used to update the state of searchTerm.
I added searchTerm: '' to the end of the searchJokes method, which fetches a search according to the search term, as you can see in the parent component code below.
Parent component:
class App extends Component {
constructor() {
super();
this.state = {
searchTerm: '',
jokes: [],
isFetchingJokes: false,
isSearch: false
};
this.onSearchChange = this.onSearchChange.bind(this);
this.randomizeJokes = this.randomizeJokes.bind(this);
this.searchJokes = this.searchJokes.bind(this);
}
randomizeJokes() {
this.setState({
isFetchingJokes: true,
isSearch: false
});
fetch(
'https://icanhazdadjoke.com/',
{
method: 'GET',
headers: {
Accept: 'application/json'
}
})
.then(response => response.json())
.then(json => {
let joke = json.joke;
this.setState({
joke,
isFetchingJokes: false
});
});
}
searchJokes(limit = 15) {
// If nothing entered, user gets "Please fill out this field" message due to "required" attribute on input element
if (this.state.searchTerm !== '') {
this.setState({
isFetchingJokes: true,
isSearch: true
});
fetch(
`https://icanhazdadjoke.com/search?term=${
this.state.searchTerm
}&limit=${limit}`,
{
method: 'GET',
headers: {
Accept: 'application/json'
}
})
.then(response => response.json())
.then(json => {
let jokes = json.results;
this.setState({
jokes,
isFetchingJokes: false,
searchTerm: '' // <-- DOESN'T CLEAR INPUT
});
});
}
}
onSearchChange(value) {
this.setState({ searchTerm: value });
}
jokeRender() {
return (
<div>
{this.state.isSearch ?
<ul>{this.state.jokes.map(item => <li key={item.id}>{item.joke}</li>)}
</ul> : <p className="random-joke">{this.state.joke}</p>}
</div>
);
}
render() {
return (
<div>
<h1>Dad Jokes</h1>
<RetrievalForm
onFormSubmit={this.searchJokes}
onInputChange={this.onSearchChange}
isSearching={this.state.isFetchingJokes}
onRandomize={this.randomizeJokes}
/>
{this.state.isFetchingJokes ? <p className="searching-message">Searching for jokes...</p> : this.jokeRender()}
</div>
);
};
}
Child component:
const RetrievalForm = props => {
const onSubmit = e => {
// Prevents GET request/page refresh on submit
e.preventDefault();
props.onFormSubmit();
};
return (
<>
<form onSubmit={onSubmit}>
<input
type="text"
placeholder="Enter search term..."
onChange={e => props.onInputChange(e.target.value)}
required
/>
<div>
{/* Specifying type here since it's good practice; different browsers may use default types for buttons */}
<button type="submit" disabled={props.isSearching}>Search</button>
{/* type="button" stops input validation message from being displayed (on Firefox) when randomize button is clicked without anything entered */}
<button type="button" onClick={props.onRandomize} disabled={props.isSearching} className="randomize-button">
Randomize
</button>
</div>
</form>
</>
);
};
Any help would be greatly appreciated.
You need to pass your searchTerm down to the RetrievalForm and in that input set value={searchTerm} so that it's value will be bound to that state.
Basically, you need to store the input value in the component's state. When onSubmit is called, we should revert that value to an empty string.
Example with some React Hooks goodness:
import React, { Component, useState } from 'react';
const RetrievalForm = props => {
const [searchTerm, setSearchTerm] = useState('');
const onChange = e => {
const { value } = e.target;
props.onInputChange(value);
setSearchTerm(value)
}
const onSubmit = e => {
// Prevents GET request/page refresh on submit
e.preventDefault();
props.onFormSubmit();
setSearchTerm('');
};
return (
<>
<form onSubmit={onSubmit}>
<input
type="text"
value={searchTerm}
placeholder="Enter search term..."
onChange={onChange}
required
/>
<div>
{/* Specifying type here since it's good practice; different browsers may use default types for buttons */}
<button type="submit" disabled={props.isSearching}>
Search
</button>
{/* type="button" stops input validation message from being displayed (on Firefox) when randomize button is clicked without anything entered */}
<button type="button" onClick={props.onRandomize} disabled={props.isSearching} className="randomize-button">
Randomize
</button>
</div>
</form>
</>
);
};
Example here: https://stackblitz.com/edit/react-db5ire

Categories