Use array of strings in React Hook Form - reactjs

In a form that I am making the material that is being created in the form should have multiple width options that can be added. This means that I will have a text input where the user can add an option, and when this option is added, it should be added to the React Hook Form widthOptions array, without using the regular react state. How would one do this? How do you add an item to the total React Hook Form state, I only see options for just one input field corresponding to a property.
This is how i would do it using the regular React state
import { TrashIcon } from "#heroicons/react/24/outline";
import React, { useRef, useState } from "react";
const Test = () => {
const [widthOptions, setWidthOptions] = useState<string[]>([]);
const inputRef = useRef<HTMLInputElement>(null);
const removeWidthOption = (widthOption: string) => {
setWidthOptions(widthOptions.filter((option) => option !== widthOption));
};
const addWidthOption = (widthOption: string) => {
setWidthOptions([...widthOptions, widthOption]);
};
const editWidthOptions = (widthOption: string, index: number) => {
const newWidthOptions = [...widthOptions];
newWidthOptions[index] = widthOption;
setWidthOptions(newWidthOptions);
};
return (
<div>
<input type="text" ref={inputRef} />
<button onClick={() => addWidthOption(inputRef?.current?.value)}>
Add Width Option
</button>
{widthOptions.map((option, index) => (
<div className="flex">
<input
type="text"
value={option}
onChange={() => editWidthOptions(option, index)}
/>
<button type="button" onClick={() => removeWidthOption(option)}>
<TrashIcon className="w-5 h-5 mb-3 text-gray-500" />
</button>
</div>
))}
</div>
);
};
export default Test;

You can just the controller component for this as for all other fields.
Since you have not shared any of you code here is a generic multi-select
<Controller
name={name}
render={({ field: { value, onChange, ref } }) => {
return (
// You can use whatever component you want here, the you get the value from the form and use onChange to update the value as you would with a regular state
<Test
widthOptions={value}
setWidthOptions={onChange}
/>
);
}}
/>;
https://react-hook-form.com/api/usecontroller/controller/
And in you Test component remove the state and get the props instead
const Test = ({widthOptions, setWidthOptions}) => {
const inputRef = useRef<HTMLInputElement>(null);
.
.
.

Related

React js: How to Update A State (Of Array) From <input /> onChange method

I am trying to figure how to update an array state and output the changes in input for my React Js Project.
I am trying to display multiple input components on the screen by mapping an array with the input component as it's child. I want to change the value of individual item with onChange()function and want to view the changes inside the input component.
import { useState } from "react";
import "./styles.css";
export default function App() {
const [array, setArray] = useState(["1", "2", "3"]);
return (
<div className="App">
{array.map((item, index) => (
<input
type="text"
value={array[index]}
onInput={(e) => {
setArray((array) => {
array[index] = e.target.value;
return array;
});
}}
/>
))}
</div>
);
}
The Updates are not being displayed.
This should work :
<input
key={index}
type="text"
value={array[index]}
onInput={(e) => {
setArray((prevArr) => {
const result = [...prevArr];
result[index] = e.target.value;
return result;
});
}}
/>

React form - maintaing state when I don't have a fixed value of fields

So I created a component which is responsible with opening a form to add data. Here it is:
import Button from "#mui/material/Button";
import TextField from "#mui/material/TextField";
import { useState } from "react";
import "./CreateRow.css";
type Props = {
fields: string[]
}
export default function CreateRow(props: Props) {
function sendData(event: any) {
event.preventDefault();
console.log(event);
}
const [creatingRow, setCreatingRow] = useState(false);
return (
<>
<Button variant="contained"
onClick={() => setCreatingRow(!creatingRow)}
className="adauga-button"
>Adauga</Button>
{
creatingRow && (
<form onSubmit={sendData} className="create-form">
{
props.fields.map((field, index) =>
{
return (
<TextField
key={index}
required
id="outlined"
name={field}
label="Nume"
placeholder="Scrie"
/>
)
}
)
}
<Button variant="contained"
className="trimite-button"
>Finalizeaza</Button>
</form>
)
}
</>
);
}
the fields that will be rendered for the form, will be passed as props, in a string array (as you can see)
So how will I create my hooks for controlling the form data?
You need to add a state variable to be passed to the material-UI text field and attach an onchange method to update the value.
But since it seems there might be multiple text fields there needs to be an individual state for each of those text fields. Create a function component as below in your file.
function TextFieldWithState(props) {
const [value, setValue] = useState(props?.value ? props.value : ""); //checking if value is there in props if so assigning it else
const updateValue = (e) => setValue(e.target.value);//function to update the state value.
return <TextField {...props} value={value} onChange={updateValue} />;
}
After creating the component call it inside the props.fields.map as below,
props.fields.map((field, index) => {
return (
<TextFieldWithState
key={index}
required
id="outlined"
name={field}
label="Nume"
placeholder="Scrie"
/>
)
})

react js myfn is not a function when called from a button

I've just started learning about react js and this is my first react js app. I'm using api to fetch the data. so far it works, but now I want to add a search keyword to the function that is acquired from a search bar component.
here's my code:
SearchBar.js
const SearchBar = ({ getUsers }) => {
return (
<div className="is-flex flex-align-items-center mb-3">
<input type="text" id="query" className="input search-input" placeholder="search keyword"/>
<Button className="search-btn ps-3 pe-3"
onClick={() => getUsers(document.querySelector('#query').value)}>
<FontAwesomeIcon icon={faMagnifyingGlass} />
</Button>
</div>
);
};
MasterUser.js
import { useState, useEffect } from "react";
import SearchBar from "./SearchBar";
const MasterUser = () => {
const [users, setUsers] = useState([]);
useEffect(() => {
getUsers();
}, []);
const getUsers = async (query='') => {
console.log('get users', query);
try {
let myurl = 'http://localhost:8080/users';
const response = await fetch(myurl);
const data = await response.json();
setUsers(data);
setIsLoading(false);
} catch (e) {
console.log(e.getMessage());
}
};
return (
<div>
<SearchBar onClick={getUsers}/>
</div>
);
};
when the app loads, the console log says get users <empty string> and it returns all the users as expected, but when I clicked on the search button (magnifyingGlass) it gives an error Uncaught TypeError: getUsers is not a function.
any help is appreciated..
<SearchBar onClick={getUsers}/>
You have named the prop onClick not getUsers. That's why you get that error.
Yeah, accessing dom element value using selectors (e.g. document.querySelector('#query').value) is also not typical react. Read about controlled form elements (save form element value in state).
Make your searchBar component more reactive like so
const SearchBar = ({ getUsers }) => {
const [searchValue,setSearchValue]=useState('');
return (
<div className="is-flex flex-align-items-center mb-3">
<input type="text" id="query" className="input search-input" placeholder="search keyword" value={searchValue} onChange={(e)=>setSearchValue(e.target.value)}/>
<Button className="search-btn ps-3 pe-3"
onClick={() => getUsers(searchValue)}>
<FontAwesomeIcon icon={faMagnifyingGlass} />
</Button>
</div>
);
};

react: manually trigger input component synthetic change event

I'm trying to build an input component with a clear button using react#17
import { useRef } from 'react';
const InputWithClear = props => {
const inputRef = useRef();
return (
<div>
<input
ref={inputRef}
{...props}
/>
<button
onClick={() => {
inputRef.current.value = '';
inputRef.current.dispatchEvent(
new Event('change', { bubbles: true })
);
}}
>
clear
</button>
</div>
);
};
using this component like:
<InputWithClear value={value} onChange={(e) => {
console.log(e); // I want to get a synthetic event object here
}} />
but the clear button works once only when I did input anything first, and stop working again.
if I input something first and then click the clear button, it does not work.
why not using?
<button
onClick={() => {
props.onChange({
target: { value: '' }
})
}}
>
clear
</button>
because the synthetic event object will be lost
So, how do I manually trigger a synthetic change event of a react input component?
Try this approach,
Maintain state at the parent component level (Here parent component is App), onClear, bubble up the handler in the parent level, and update the state.
import React, { useState } from "react";
import "./styles.css";
const InputWithClear = (props) => {
return (
<div>
<input {...props} />
<button onClick={props.onClear}>clear</button>
</div>
);
};
export default function App() {
const [value, setValue] = useState("");
return (
<div className="App">
<h1>Hello CodeSandbox</h1>
<InputWithClear
value={value}
onChange={(e) => {
console.log(e); // I want to get a synthetic event object here
setValue(e.target.value);
}}
onClear={() => {
setValue("");
}}
/>
</div>
);
}
Working code - https://codesandbox.io/s/youthful-euler-gx4v5?file=/src/App.js
you should use state to control input value rather than create useRef, that's the way to go. you can use a stopPropagation prop to control it:
const InputWithClear = ({value, setValue, stopPropagation = false}) => {
const onClick = (e) => {
if(stopPropagation) e.stopPropagation()
setValue('')
}
return (
<div>
<input
value={value}
onChange={e => setValue(e.target.value)}
/>
<button
onClick={onClick}
>
clear
</button>
</div>
);
};
export default function App() {
const [value, setValue] = useState('')
return (
<div className="App">
<InputWithClear value={value} setValue={setValue} stopPropagation />
</div>
);
}

React: Change field's value from button

I have this fairly simple application that has multiple inputs, and only has state for the current field, and the current field's input. When you click on an input, it sets the "current field" and "current input" accordingly, not resetting on blur.
Is there any way to implement having a button that resets the current field's value, as shown below?
import React, { useState } from 'react'
const FieldTest = () => {
const [currentField, setCurrentField] = useState(1)
const [currentInput, setCurrentInput] = useState('A')
const fields = [...Array(4)].map((current, i) => (
<input
key={i}
type='text'
onFocus={e => {
setCurrentField(i + 1)
setCurrentInput(e.target.value)
}}
onChange={e => setCurrentInput(e.target.value)}
defaultValue={String.fromCharCode(64 + i + 1)}
/>
))
return (
<>
<h1>Field Test</h1>
<p>Current Field: {currentField}</p>
<p>Current Input: {currentInput}</p>
{fields}
<button
onClick={e => {
/*Is there any way to implement this by only changing this onClick?*/
}}
>
Reset Current Field's Input
</button>
</>
)
}
export default FieldTest
This could easily be accomplished by refactoring this into a state for each input, or a ref for each input, but if there's any way I could not implement that (as it makes adding variable amounts of inputs a lot easier) then let me know.
Or if there were a completely different way to implement this that also allowed for variable amounts of inputs, let me know!
Thanks!
You can use keep one ref and change its .current value based on the element that is focused.
import React, { useState, useRef } from "react";
export default function App() {
const [currentField, setCurrentField] = useState(1);
const [currentInput, setCurrentInput] = useState('A');
const curInputRef = useRef(null);
const fields = [...Array(4)].map((current, i) => {
return (
<input
key={i}
type="text"
onFocus={(e) => {
setCurrentField(i + 1);
setCurrentInput(e.target.value);
curInputRef.current = e.target; //make the ref point to the focused input
}}
onChange={(e) => setCurrentInput(e.target.value)}
defaultValue={String.fromCharCode(64 + i + 1)}
/>
);
});
return (
<>
<h1>Field Test</h1>
<p>Current Field: {currentField}</p>
<p>Current Input: {currentInput}</p>
{fields}
<button
onClick={(e) => {
if (!curInputRef.current) return;
curInputRef.current.value = ''; //change the value of the focused input to be ''
/*Is there any way to implement this by only changing this onClick?*/
}}
>
Reset Current Field's Input
</button>
</>
);
}
The script in action:
https://codesandbox.io/s/admiring-saha-2uo2f?file=/src/App.js

Resources