how to use useRef for the attribute htmlFor? - reactjs

I want to customize the input tag for file upload.
This is my code. Here for the attribute htmlFor, I am giving id of the input tag.Then it is working. But instead I want to use useRef ref. How can I do that ? If I follow the below method, it will be problematic if I render this component more than once. right ?
const App = () => {
const inputRef = useRef(null);
const [file, setFile] = useState(null);
return (
<>
<input
ref={inputRef}
accept=".pdf"
style={{ display: "none" }}
id="raised-button-file"
multiple
type="file"
onChange={e => {
setFile(e.target.files[0]);
}}
/>
<label htmlFor="raised-button-file">
<button component="span">
<span>file</span>
</button>
</label>
</>
);
};

Another way of using <label> tag is by wrapping your element as a child without specifying an id for it.
<label>
<input
accept=".pdf"
style={{ display: "none" }}
multiple
type="file"
onChange={e => {
setFile(e.target.files[0]);
}}
/>
<span>File</span>
</label>
If you prefer to open your file input dialog with your ref, you can do like this.
const handleOpenFileInput = () => {
inputRef.current.click();
};
<label onClick={handleOpenFileInput}>
<button>file</button>
</label>
<input
ref={inputRef}
accept=".pdf"
style={{ display: "none" }}
multiple
type="file"
onChange={e => {
setFile(e.target.files[0]);
}}
/>

If you use userRef it won't solve your problem. The problem is in the label and the htmlFor attribute. It constantly gets the inputs whose ids match the htmlFor attribute, and since your render the component multiple times, it always gets the first match.
I would simply pass the id of each component as a property, so that each time the label matches the right input. I would change the code to look more like this:
const Form = ({id}) => {
const onChangeInput = e => {
const [file] = e.target.files
}
return (
<form>
<div>
<input type="file" id={id} name="file" className="my-input" accept="application/pdf" style={{display:"none"}} onChange={onChangeInput} multiple/>
<label htmlFor={id}>Upload</label>
</div>
</form>
)
}
function App() {
return (
<div className="App">
<Form id="form1"/>
<Form id="form2"/>
<Form id="form3"/>
</div>
);
}
To make sure that each document has been uploaded correctly, I passed a className attribute to the inputs so I can get all of them. Running this code, I find all files that I have uploaded
Array.from(document.querySelectorAll('.my-input')).map(v => v.files[0].name)

Related

How to pass state props to another component

I want to pass a state variable to another component but i don't get it what i'm doing wrong.
I have a component for radio inputs and I need to pass that taskLabel to Form component.
path is components/inputs/index.js
const TaskLabel = (props) => {
const [taskLabel, setTaskLabel] = useState('');
return (
<div label={props.taskLabel} >
<input
type='radio'
name='label'
value='urgent'
onChange={(e) => setTaskLabel(e.target.value)}
/>
<input
type='radio'
name='label'
value='not-urgent'
onChange={(e) => setTaskLabel(e.target.value)}
/>
</div>
);
};
i want to receive the taskLabel value, to use it in submitHandler function.
components/form/index.js
const Form = ({taskLabel}) => {
return (
<form onSubmit={submitHandler}>
<input
type='text'
placeholder='Text here'
className='form-input'
value={task}
onChange={(e) => {
setTask(e.target.value);
}}
/>
<TaskLabel taskLabel={taskLabel} />
</form>
)
}
This is what i tried, to pass taskLabel props from label={taskLabel}.
You need to move your state, to Form component, like this:
const [labelProp, setLabelProp] = useState("");
Your TaskLabel component should be
<TaskLabel label={{ labelProp, setLabelProp }} />
That means, you send label, as a prop to TaskLabel component.
In TaskLabel component you need to recive the prosp, by passing to component {label}.
Next, for every input use onChange={(e) => label.setLabelProp(e.target.value)}.
Edited sandbox => https://codesandbox.io/s/laughing-proskuriakova-dhi568?file=/src/components/task-label/index.js
Generally speaking, the concept is "data-down, actions-up". So if you want to pass data down to a lower-level component, you just pass a prop. If you want to update a value from a lower-level component to a higher-level component, you could pass a setter function down as a prop and call that.
Note I just call it taskLevelProp for a little more clarity. You should probably use a better name.
TaskLabel (lower level component)
/* It looks like TaskLabelProp gets passed in from something else.
Otherwise, you would use useState to get your setter function */
const TaskLabel = ({taskLabelProp, setTaskLabelProp}) => {
return (
<div label={props.taskLabel} >
<input
type='radio'
name='label'
value='urgent'
onChange={(e) => setTaskLabelProp(e.target.value)}
/>
<input
type='radio'
name='label'
value='not-urgent'
onChange={(e) => setTaskLabelProp(e.target.value)}
/>
</div>
);
};
Form (higher level component)
const Form = () => {
const [taskLabelProp, setTaskLabelProp] = useState('');
return (
<form onSubmit={submitHandler}>
<input
type='text'
placeholder='Text here'
className='form-input'
value={task}
onChange={(e) => {
setTask(e.target.value);
}}
/>
<TaskLabel taskLabel={taskLabelProp, setTaskLabelProp} />
</form>
)
}
Let me know if this answers your question.
EDIT: Made Form use useState. Based off your code, I was assuming you were using useState at the app level.
function App() {
const [task, setTask] = useState("");
const [taskLabelProp, setTaskLabelProp] = useState("");
const handleChange = (e) => {
setTaskLabelProp(e.target.value);
};
const submitHandler = (e) => {
e.preventDefault();
};
return (
<div className="App">
<form onSubmit={submitHandler}>
<input
type="text"
placeholder="Text here"
className="form-input"
value={task}
onChange={(e) => {
setTask(e.target.value);
}}
/>
<TaskLabel
onChange={handleChange}
taskLabelProp={taskLabelProp}
taskLabel="select"
/>
<button type="submit">fs</button>
</form>
</div>
);
}
export default App;
const TaskLabel = ({ taskLabel, onChange, taskLabelProp }) => {
return (
<div label={taskLabel}>
<input
type="radio"
name="label"
value="urgent"
onChange={(e) => onChange(e)}
checked={taskLabelProp === "urgent"}
/>
urgent
<input
type="radio"
name="label"
value="not-urgent"
onChange={(e) => onChange(e)}
checked={taskLabelProp === "not-urgent"}
/>
not-urgent
</div>
);
};
export default TaskLabel;

React - how to target value from a form with onClick

New to react and currently working on a project with a backend.
Everything functions correctly apart from targeting the value of user selection.
basically whenever a user enters a number the setId is saved properly to the const with no problems while using the onChange method.
this method would render my page every change on text.
I am trying to save the Id only when the user clicks the button. however,
event.target.value does not work with onClick.
I tried using event.currentTarget.value and this does not seem to work.
Code:
<form onSubmit={handleSubmit}>
<label>Company ID</label>
<input value={id} onChange={(e) => setId(e.target.value)} type="number" />
{/* <button value={id} type="button" onClick={(e) => setId(e.currentTarget.value)}>Search</button> */}
</form>
Handle Submit:
const handleSubmit = (e) => {
e.preventDefault();
console.log(id)
}
is there a way of doing this with onclick? since I wouldn't like my component to render on every typo and only once a user has clicked the button.
Componenet:
interface GetOneCompanyProps {
company: CompanyModel;
}
interface RouteParam {
id: any;
}
interface CompanyById extends RouteComponentProps<RouteParam> {
}
function GetOneCompany(): JSX.Element {
const [id, setId] = useState('4');
const [company, setCompany] = useState<any>('');
const handleSubmit = (e) => {
e.preventDefault();
console.log(id)
}
async function send() {
try {
const response = await axios.get<CompanyModel>(globals.adminUrls.getOneCompany + id)
store.dispatch(oneCompanyAction(response.data));
console.log(response);
const company = response.data;
setCompany(company)
} catch (err) {
notify.error(err);
}
}
useEffect(() => {
send();
}, [id]);
return (
<div className="getOneCompany">
<h1>hi </h1>
<form onSubmit={handleSubmit}>
<label>Company ID</label>
<input value={id} onChange={(e) => setId(e.target.value)} type="number" />
{/* <button value={id} type="button" onClick={(e) => setId(e.currentTarget.value)}>Search</button> */}
</form>
<div className="top">
</div>
<br/>
Company: {id}
<br/>
Client Type: {company.clientType}
<br/>
Company Name: {company.name}
<br/>
Email Adress: {company.email}
<br/>
</div>
);
}
export default GetOneCompany;
Hope I am clear on this.
Thanks.
You can turn your input from being a controlled input to an uncontrolled input, and make use of the useRef hook. Basically, remove most of your attributes from the input element, and grab the current value of the input form on click of the button. From there, you can do whatever you want with the input value.
const inputRef = useRef()
...other code
<form onSubmit={handleSubmit}>
<label>Company ID</label>
<input type="number" ref={inputRef} />
<button value={id} type="button" onClick={() => console.log(inputRef.current.value)}>Search</button>
</form>
...other code
I'm afraid to say that here onChange is mandatory as we also are interested in the value which we set by setId. onClick can't be used as we can't set the value in the input.
Hope I'm clear.
Thankyou!

how to invoke and change the value of a component on handleSubmit in react

I am coding with react. I created a Form and on submit I want to send the value of the input field as parameter of another react component, here is "cypher_query" of . Here is my code, it's not working, I don't know where I did mistake. Please help 🙏
const AppGraph = () => {
const [query, setQuery] = useState("");
const handleSubmit = (evt) => {
evt.preventDefault();
}
return (
<div>
<form id="myForm" onSubmit={handleSubmit} >
<div className="App" style={{ fontFamily: "Quicksand" }}>
<label htmlFor="CommentsOrAdditionalInformation">enter your query</label>
<input
name = "requete"
type="text"
id="CommentsOrAdditionalInformation"
value = {query}
onChange={e => setQuery(e.target.value)}
>
</input>
<input type = "submit" value="Submit" className="btn_submit" alt = "submit Checkout"/>
</div>
<p>{query}</p>
</form>
{<ResponsiveNeoGraph
containerId={"id0"}
neo4jUri={NEO4J_URI}
neo4jUser={NEO4J_USER}
neo4jPassword={NEO4J_PASSWORD}
cypher_query={query}
/>}
</div>
);
};
export default AppGraph;

Warning: A component is changing a controlled input of type file

In an application using react, I have a form with radio input, which chooses which component will render, but when I change the radio option this warning shows up -
"A component is changing an uncontrolled input of type file to be controlled. Input elements should not switch from uncontrolled to controlled (or vice versa). Decide between using a controlled or uncontrolled input element for the lifetime of the component"
Form
import React, { useState } from "react";
export default () => {
const [link, setLink] = useState("");
const [arquivo, setArquivo] = useState("");
const [forma_envio, setFormaEnvio] = useState("");
return(
<React.Fragment>
<div className="form-check ">
<input
type="radio"
className="form-check-input"
name="forma"
id="forma1"
value="File"
checked={forma_envio === "File"}
onChange={(e) => {
setFormaEnvio(e.target.value);
}}
/>
</div>
<div className="form-check ">
<input
type="radio"
className="form-check-input"
name="forma"
id="forma2"
value="Link"
checked={forma_envio === "Link"}
onChange={(e) => {
setFormaEnvio(e.target.value);
}}
/>
</div>
{forma_envio === "File" ? (
<input
type="file"
className="form-control-file form-control"
id="arquivo"`
onChange={(e) => {
e.preventDefault();
handleUpload(e.target.files[0]);
}}
/>
) :forma_envio === "Link" (
<input
value={link}
type="text"
className="form-control"
id="link"
onChange={(e) => {
e.preventDefault();
setLink(e.target.value);
}}
/>
):
("")}
</React.Fragment>
);
}
All states are starting with "".
Everything works fine, but I still can't figure how to fix this warning.
Make sure the initial value of link is not null or undefined, but an empty string, like:
// const [link, setLink] = React.useState() // 🔴 will cause the warning
const [link, setLink] = React.useState('') // correct
EDIT:
After the question was edited, I noticed the problem:
input tags of type "file" MUST BE UNCONTROLLED. You should not pass value or onChange to it. Instead, you should pass a reference and attach it to the input.
const arquivo = useRef(null);
return (
<React.Fragment>
// ...
<input
type="file"
ref={arquivo}
id="arquivo"
/>
</React.Fragment>
)
This is also a rare situation in which you want to hide a DOM node instead of avoiding the rendering. The File object is stored directly on the DOM, so you must keep this DOM node around, otherwise you will lose the selected files.
<input
type="file"
ref={arquivo}
id="arquivo"
style={{ display: forma_envio !== "File" && "none" }}
/>
Complete code at CodeSandbox, without warnings.

onClick in reactjs not showing items

I have to show a list of defaultValues in the search list and when I click on any of those item then it should take me to that item's component but it's not going anywhere. It's only happening with the defaultValues because as soon as I start typing, then if I click on any search result then it takes me to the desired component. what is wrong with my code?
here's the code
const [search, setSearch] = useState("");
const [showDefaultValues, setShowDefaultValues] = useState(false);
const [defaultValues] = useState({
Mumbai: true
});
{!search.length && showDefaultValues ? (
<div className="result-box">
{data
.filter((item, idx) => defaultValues[item.district])
.map((dist, idx) => (
<div
key={idx}
className="search-result"
onClick={() => {
onResultClick(dist.district);
}}
>
{highlightedText(dist.district, search)}
</div>
))}
</div>
) : null}
Just change the codes at components/search/search.js line 39 to 49
<input
placeholder="Search for a District..."
type="text"
className="search-input"
value={search}
onChange={onSearchInputChange}
onFocus={() => {
toggleDefaultValues(true);
}}
onBlur={onBlurInput}
/>
To
<input
placeholder="Search for a District..."
type="text"
className="search-input"
value={search}
onChange={onSearchInputChange}
onFocus={() => {
toggleDefaultValues(true);
}}
/>
Or simply remove line 48
To compensate this, you can add below inside your useEffect (similar to componentDidMount)
document.addEventListener("mousedown", handleInputClickOutside);
add function handleInputClickOutside to set the state to false/hide
You forgot to implement the onClick logic on the default search result items and that's why the search results work fine, while the default search items do not.
Check this link to the working codesandbox.
All i did was invoke the same onResultClick function onClick of 'District' component.
<div
className="dist"
onClick={() => {
this.props.onResultClick(item.district);
}}
>
...
</div>
Hope this solves your problem.

Resources