Populating option in react component with key value pair - reactjs

I am trying to populate the option with authors name dynamically by providing a key value pair and want the result as shown but the result is not like but instead it displays four different forms for each author.
Please refer to images for a clear view of my question.
This is what I got:
import { gql ,useQuery} from '#apollo/client';
const getAuthorsQuery = gql`
{
authors{
name
id
}
}
`;
function AddBook() {
const { loading, error, data } = useQuery(getAuthorsQuery);
if (loading) return <p>Loading...</p>;
if (error) return <p>Error :(</p>;
console.log(data)
return data.authors.map(author => {
return (
<form id="add-book">
<div className="field">
<label>Book name:</label>
<input type="text" />
</div>
<div className="field">
<label>Genre:</label>
<input type="text" />
</div>
<div className="field">
<label >Author:</label>
<select >
<option key={author.id} value ={author.id}>Select Authors</option>
{author.name}
</select>
</div>
<button>+</button>
</form>
)
})

Instead of using map for entire form use at options alone.
{data.authors.map(author => <option key={author.id} value ={author.id}>Select Authors</option>)}

You have your order of operations wrong. You have placed the loop way too early in the flow of your code, and are therefore creating all of the JSX for the entire form for each option.
You can declare JSX as a variable earlier in the method and then use it in your final return JSX by wrapping it in {}
Also, you want to put the author.name inside of the option, and your Select Author option should have an empty value; like this:
let options = [];
for (let author in authors) {
options.push(<option key={author.id} value={author.id}>author.name</option>);
}
...
<select>
<option value="">Select Authors</option>
{options}
</select>

Related

Add a search input inside a select without using a library

I feel like there is a simple solution to this but I've been stuck on it for a while.
This is what I did:
export default function App() {
const [searchTerm, setSearchTerm] = useState('');
return (
<div>
<div>
<input type="text" onChange={(e) => setSearchTerm(e.target.value)} />
</div>
<select>
<option defaultOption>Choose option</option>
{SelectOptions.filter(({ val }) => val.includes(searchTerm)).map(
({ val, id }) => {
return (
<option key={id} value={id}>
{val}
</option>
);
}
)}
</select>
</div>
);
}
sandbox: https://stackblitz.com/edit/react-qx5fym?file=src%2FApp.js
As you can see I have an input above my select and it works. If I search something, the select option shorten the list. However, I'm struggling on trying to get the input inside the select.
I want to be able to get this:
Exclude the styling that isn't the goal right now.
Anyone know how to achieve this?

React getting empty state

I am trying to create an advanced regular expression search application to let me search for some content and replace it in other lines but I am struggling with a much easier task: Cannot delete the rules since the rules state seems to be empty:
export default function App() {
const [displayNewRule, setDisplayNewRule] = useState(false);
const [replaceType, setReplaceType] = useState("line");
const [rules, setRules] = useState([]);
function handleClick() {
setDisplayNewRule(true);
}
function handleSubmit(e) {
e.preventDefault();
const rule = {
name: e.target.name.value,
sourceSearchPattern: e.target.sourceSearchPattern.value,
replaceType,
value: e.target.value.value
};
if (replaceType === "occurrence") {
rule.targetSearchPattern = e.target.targetSearchPattern.value;
rule.location = e.location.value;
}
let $rules = rules;
$rules.push(rule);
setRules($rules);
e.target.reset();
handleReset();
}
function handleReset() {
setDisplayNewRule(false);
}
function deleteRule(i) {
let $rules = rules;
$rules.splice(i, 1);
setRules($rules);
// this gets an empty rules
}
return (
<div className="App">
<form
onSubmit={handleSubmit}
onReset={handleReset}
style={{ display: displayNewRule || "none" }}
>
<h3>Create new rule</h3>
<div className="input-group">
<label htmlFor="name">Name:</label>
<input type="text" id="name" name="name" required />
</div>
<div className="input-group">
<label htmlFor="sourceSearchPattern">Source search pattern:</label>
<input
type="text"
id="sourceSearchPattern"
name="sourceSearchPattern"
required
/>
</div>
<div className="input-group">
<label htmlFor="replaceType">Replace type:</label>
<select
id="replaceType"
name="replaceType"
onChange={(e) => {
setReplaceType(e.target.value);
}}
required
>
<option value="">Select item</option>
<option value="line">Replace line</option>
<option value="occurrence">Replace occurrence</option>
</select>
</div>
{replaceType === "occurrence" && (
<div className="input-group">
<label htmlFor="targetSearchPattern">Target search pattern:</label>
<input
type="text"
id="targetSearchPattern"
name="targetSearchPattern"
required
/>
</div>
)}
<div className="input-group">
<label htmlFor="value">Value:</label>
<input type="text" id="value" name="value" required />
</div>
{replaceType === "occurrence" && (
<div className="input-group">
Occurrence location:
<div className="option-group">
<input
type="radio"
id="next-occurrence"
name="location"
value="next"
required
/>
<label htmlFor="next-occurrence">Next occurrence</label>
</div>
<div className="option-group">
<input
type="radio"
id="previous-occurrence"
name="location"
value="previous"
/>
<label htmlFor="previous-occurrence">Previous occurrence</label>
</div>
</div>
)}
<button type="submit">Submit</button>
<button type="reset">Cancel</button>
</form>
<button onClick={handleClick}>Add rule</button>
<div className="rules">
<h3>Rules</h3>
{rules.map((rule, i) => ( // These rules are being displayed.
<div className="rule" key={i + 1}>
<h5>
{rule.name}
<button
onClick={() => {
deleteRule(i);
}}
>
Delete rule
</button>
</h5>
<p>Replace type: {rule.replaceType}</p>
<p>Source search pattern: {rule.sourceSearchPattern}</p>
</div>
))}
</div>
</div>
);
}
Any clues?
Instead of
let $rules = rules;
$rules.push(rule);
setRules($rules);
try
setRules(oldRules => [...oldRules, rule]);
You're mutating state in your current code, which is a huge anti-pattern. I'm not sure if it's causing the issue you're seeing, but changing to this pattern will make it easier to debug and eliminated a variable. Also change your deleteRule function so that it's likewise not mutating state (make a copy of state with [...rules] and operate on that before setting the new rules).
tl;dr - your component does not re-render at all.
More description: $rules keeps the reference of state object rules (it's the same instance). When calling setRules shallow comparison goes in and prevents you from updating the component (the new value is exactly the same as the current one, because of the direct mutation).
Someone would ask why shallow comparison prevents component from re-rendering since it works only with primitives (which an array is not). The thing is shallow comparison still works on objects that holds the same reference.
const array = [];
array === array // true;
Code with step-by-step description:
function handleSubmit() {
let $rules = rules; // $rules holds the reference and is the same instance as rules
$rules.push(rule); // directly updated state (mutated)
// value of $rules and rules is again exactly the same!
setRules($rules); // the component is not even updated because of the
// shallow comparison - $rules and rules is the same instance
// with exactly the same value
}
Solution is to avoid direct mutation.
setRules((oldRules) => [...oldRules, rule]); as #Nathan posted above.

redirection to confirmation page using react hook form

I am not able to redirect to the confirmation page on submitting the form. Can someone help me with this?
This is a form that I have created using react hook form library. I am a newbie in react js.. trying the first time in react. Already searched through the internet
but didn't find anything.
Thanks in advance.
date: yup.string().required(),
amount: yup.number().positive().integer().required(),
});
export default function App(){
const { register, handleSubmit, watch, formState: { errors } } = useForm({ resolver: yupResolver(schema)});
const onSubmit = data => {
//this.props.history.push('/home')
//alert(JSON.stringify(data));
window.location.href = '/tranfser-confirmation?from=${from}';
console.log(data);
}
const watchShowRecurring = watch("recurring", false); // you can supply default value as second argument
const watchAllFields = watch(); // when pass nothing as argument, you are watching everything
const watchFields = watch(["recurring", "recurringType"]); // you can also target specific fields by their names
const accountTo = [
{ value: 'A', name: 'A' },
{ value: 'B', name: 'B' }
];
const payeeList = [
{ value: 'Payee1', name: 'Payee 1' },
{ value: 'Payye 2', name: 'Payee 2' }
];
return(
<div className='form-content-right'>
<form onSubmit={handleSubmit(onSubmit)} className='form'>
<h1>
Get started with us today! Transfer amount to another account.
</h1>
<div className='form-inputs'>
<label className='form-label'>From</label>
<select className='form-input' name="from" {...register("from")}>
{accountTo.map((e, key) => { return <option key={key} value={e.value}>{e.name}</option>; })}
</select>
{errors.from && <p>{'From is required'}</p>}
</div>
<div className='form-inputs'>
<label className='form-label'>To (If Payee is not in the list, Please add payee)</label>
<select className='form-input' name="to" {...register("to")}>
{payeeList.map((e, key) => { return <option key={key} value={e.value}>{e.name}</option>; })}
</select>
{errors.to && <p>{'To is required'}</p>}
</div>
<div className='form-inputs'>
<label className='form-label'>Amount</label>
<input className='form-input' type="number" placeholder="amount" name="amount" {...register("amount", { required: true })}/>
{errors.amount && <p>{'Amount is not valid'}</p>}
</div>
<div className='form-inputs'>
<label className='form-label'>Date</label>
<input className='form-input' type="Date" placeholder="date" name="date" {...register("date", { required: true })}/>
{errors.date && <p>{'Date is required'}</p>}
</div>
<div className='form-inputs'>
<input name="recurring" type="checkbox" id="recurring" {...register("recurring")}/>
<label for="recurring" className="form-label">Recurring</label>
</div>
{watchShowRecurring && (
<div className='form-inputs'>
<select className='form-input' name="recurringType" {...register("recurringType")}>
<option value="Select">--Select--</option>
<option value="Daily">Daily</option>
<option value="Weekly">Weekly</option>
<option value="Monthly">Monthly</option>
<option value="Monthly">Yearly</option>
</select>
</div>
)}
<button className='form-input-btn' type='submit'>
Continue
</button>
</form>
</div>
)
}```
Refer this : GIF with the url you are appending to redirect, Its working but I think maybe It cant find the route you are appending
Use template strings in JS not ' ' because it cant access the data.from value, its directly passed as a string. use this /tranfser-confirmation?from=${data.from}
Data object has its key as from , directly accessing from would not work and passing string not value would cause problem
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Template_literals You can read it here
Redirect is working properly because if i gave window.location.href = 'https://www.google.com/';
This is the output: added google url and it redirected properly
YOu can check the routing in your react app and proper string append might help
React Router is a good fix for routing in SPA. Because it loads only the component without page refreshing
You can refer here: https://reactrouter.com/web/guides/quick-start

Firebase - set default value in select field after form submit

I have simple form with dropdown select menu. How do I reset the select field back to show first option after submission ( ---Select category---)? I have tried setCategory("") or setCategory("---Select category---") but it keeps showing category i have picked.
const [category, setCategory] = useState("");
function formSubmit(e) {
e.preventDefault();
firebase
.firestore()
.collection("posts")
.add({
category,
})
.then(() => {
setCategory("");
});
}
<form onSubmit={formSubmit}>
<div>
<label htmlFor="category">Post Category</label>
<select
name="category"
onChange={(e) => setCategory(e.currentTarget.value)}
>
<option value="">--- Select category ---</option>
<option value="react">React</option>
<option value="CSS">CSS</option>
<option value="misc">Misc</option>
</select>
<div>
<button type="submit">Add</button>
</div>
</div>
After doing a little bit of research I found this questions answered here, here, and here. However, I would advice you to use the react-advanced-form library which makes this task a lot easier and will give you more features to implement on your forms.
While using this library you want to use “form.reset()” on your action button. This will let you set initial values for each field and the action will set the values of your fields to this default value. Here is an example:
import React from 'react'
import { Form } from 'react-advanced-form'
import { Input } from 'react-advanced-form-addons'
export default class Example extends React.Component {
handleSubmitted = ({ res, fields, form }) => {
form.reset() // resets "username" field to "admin"
}
render() {
return (
<Form onSubmitted={this.handleSubmitted}>
<Input
name="username"
initialValue="admin" />
</Form>
)
}
}

on submit form, couldn't get react-select values

onSubmit(values) {
console.log("i m clicked", values); /// i didn't get form values
here.
}
renderMacros() {
const { handleSubmit } = this.props;
const macrosData = this.props.macros.macros;
const categoryMacrosData = this.props.categoryMacros.category;
console.log("categoryMacrosData", categoryMacrosData);
const { open } = this.state;
if (macrosData) {
return (
<div>
<div className="form-block-5 w-form">
<form
id="macros-form"
name="macros-form"
onSubmit={handleSubmit(this.onSubmit)}
>
<div className="row">
<div className="col-sm-12">
<label>Type</label>
<Field // this works fine
name="category"
options={MACRO_TYPE_CATEGORIES}
placeholder="Type"
component={SelectInput}
set_default="true"
/>
</div>
<div className="col-sm-12">
<Field // this works fine
name="name"
placeholder="Name Ex. Follow-up template"
component={renderField}
type="text"
className="text-fields w-input"
id="macros_name"
/>
</div>
<div className="col-sm-12">
<Field // here is the problem
type="text"
name="categoryId"
options={categoryMacrosData}
placeholder="Search or add category "
component={AsyncMulti}
handleSelect={this.handleSelectChange}
/>
</div>
</div>
<button>Create Macro</button>
</form>
</div>
</div>
);
}
}
Bottom line is if i use Creatable component of react-select library, i
couldn't get selected values.
My component file: components/Multi.js
import React from "react";
import CreatableSelect from "react-select/lib/Creatable";
const MultiSelect = props => {
const { options, handleSelect } = props;
return <CreatableSelect isMulti onChange={handleSelect} options=
{options} />;
};
export default MultiSelect;
I am using react-select for select options in redux form. After
submitting form, I am unable to get form submitted values.
I am using react-select library https://react-select.com/creatable with redux form.
Given a props name in <Select> like
<Select
name = {"inputName"} // <- if you submit the form you will get vale like {"inputName":test111}
options = {[{ value: 'test111', label: 'Chocolate' }]}
/>
You are not binding handleSubmit properly as well not using refs since you are not getting the values.
I suggest you to try with the binding code in your <form> tag:
<form
id="macros-form"
name="macros-form"
onSubmit={this.handleSubmit.bind(this)}
>
Also pass refs in your field tag to get the value:
<Field
name="categoryId"
options={categoryMacrosData}
placeholder="Search or add category "
component={Multi}
handleSelect={this.handleSelectChange}
ref="categoryId"
/>
Instead of writing onSubmit function:
onSubmit(values) {
console.log("i m clicked", values); /// i didn't get form values
here.
}
Replace it with this function code:
handleSubmit(event) {
if (this.refs.categoryId !== '') {
event.preventDefault();
console.log('categoryId: ', this.refs.categoryId.value)
}
}
Hope it helps you!

Resources