How to make react input show state value? - reactjs

I have title and message variables as the local state in my React component. The idea is that when the component mounts, these two variables get initialised either with the respective values coming from currentPost object which is coming from Redux, or otherwise with an empty string.
const MyComponent = ({ currentPost }) => {
const [title, setTitle] = React.useState(currentPost.title ? currentPost.title : '');
const [message, setMessage] = React.useState(currentPost.message ? currentPost.message : '');
<form>
<div className="form-group">
<input
value={title}
onChange={evt => setTitle(evt.target.value)}
/>
</div>
<div className="form-group">
<input
value={title}
onChange={evt => setTitle(evt.target.value)}
/>
</div>
</form>
const mapStateToProps = state => ({ currentPost: state.posts.currentPost });
};
The code above is actually a modal that opens whenever I click on the edit icon of a given post. In the same time, the currentPost object on Redux state, which is empty, gets populated with the selected post. Then in the above modal component, I get currentPost from Redux state.
The problem is that the two variables always get an empty string, even when currentPost is populated with all the data I need. How can I make the input fields show the right data depending on the currentPost state?

Your logic is correct, there is nothing wrong with it. Just notice that the useState will only be defined when the component renders for the FIRST time. Your redux state is probably being updated only after the component has mounted already.
A quick way of checking that is just to console.log before defining the state.
const MyComponent = ({ currentPost }) => {
// This will probably show "undefined" in the first log
console.log('current Post', currentPost);
const [title, setTitle] = React.useState(currentPost.title ? currentPost.title : '');
const [message, setMessage] = React.useState(currentPost.message ? currentPost.message : '');
<form>
<div className="form-group">
<input
value={title}
onChange={evt => setTitle(evt.target.value)}
/>
</div>
<div className="form-group">
<input
value={title}
onChange={evt => setTitle(evt.target.value)}
/>
</div>
</form>
const mapStateToProps = state => ({ currentPost: state.posts.currentPost });
};
There are two ways of going about it. You can either use the redux state straight into the input and onChange will call a method to update that same state
OR
Use useEffect to update the local state once currentPost changes.
useEffect(() => {
setTitle(currentPost.title);
setMessage(currentPost.message);
}, [currentPost]);

try this:
const [title, setTitle] = React.useState(
currentPost.title && currentPost.title.length ? currentPost.title : ""
);
const [message, setMessage] = React.useState(
currentPost.message && currentPost.message.length ? currentPost.message : ""
);
The currentPost.title condition checks if there is a value in title or it is undefined, if you pass an empty string, then such a condition will return true, in order to check not only for the presence of a value, but also for the presence of some value in it, you can also check the length property, for strings this should work.
You can see the code here:
https://codesandbox.io/s/input-components-o02qg
UPD:
Take a look this example, i make new SandBox, on redux and TS,
https://codesandbox.io/s/serverless-silence-f19lp?file=/src/MyComponent.tsx
P.S. Only I do not understand why create internal state in components, if the state of these components is stored in the Redux store.
P.P.S. Please show more code to understand more

Related

How can I collect data by sending the register as props to the child component using React Hook Form?

Using React Hook Form, when I want to collect data by sending register as props to child component to take input value from child component, it shows 'register is not a function' error.
How can I solve this?
const { register, formState: { errors }, handleSubmit } = useForm();
const onSubmit = (data) => console.log(data);
<form onSubmit={handleSubmit(onSubmit)}>
<fieldset>
<legend className='text-[#666666]' >Status</legend>
{
statusData.map(status => <CheckboxFilter register={register} key={status._id} status={status}/>)
}
</fieldset>
</form>
here child
//CheckboxFilter component
const CheckboxFilter = ({ status, register }) => {
return (
<>
<p className='text-[#858585] mt-2 text-[14px]' >
<label htmlFor={status?.name} className='cursor-pointer' >
<input {...register("checkBoxData")} type="checkbox" name="status" id={status?.name} value={"status?.name"} /> {status?.name}
</label>
</p>
</>
);
};
I created a sandbox here codesandbox and it works perfectly.
I took your code and only changed the CheckboxFilter component:
Removed the name property (register function returns the name of the input based in the string you pass to it as a parameter, you should not override it)
Removed the value property (that was making the value of the checkbox constant, because there wasn't onChange handler that was modifying it)
Changed ...register("checkBoxData") to ...register(checkBoxData${name}) so this way you can have every checkbox value individually in the form.
Anyway, if you want to have a different behaviour than what I have assumed, let me know and I will help.
-Ado

Show an input field corresponding to radio button selection

const next = useRef(false);
const [validate,setValidate] = useState(false);
const [data, setData] = useState(healthform);
return (
<div>
<div class="hQ">
Have you travel anywhere before?
</div>
<div class="hA">
<Radio name={"travAny"} value={data.traAny} onChange={(val,name,isValid)=>{
data[name]=val;
valids[name]=isValid;
}} />
</div>
{(data.traAny && data.traAny !== "0") && <Input placeHolder="If yes, please list all countries: " value={data.traAnyDet} name="traAnyDet" onChange={(val, name, isValid) => {
data[name] = val;
valids[name] = isValid;
}} />}
</div>
);
I want to only display the input field as we select yes on radio button. I tried. But it doesn't show on the time that we pick yes. It only displays when it is already yes.
Looking at your code. I think the problem is with how you update the values. You are directly mutating the state object instaed of updating it using the setState function.
I think changing the way you update the data to a solution like the one below will fix your issue:-
const [data, setData] = useState(healthForm)
<Radio name={"travAny"} value={data.traAny} onChange={(val,name,isValid)=>{
setData(prevData=>({...prevData, [name]:val, }))
}} />
You should do the same for the valids.

Input field in react bootstrap modal re-renders modal on every key stroke

Goal: I'm trying to create a modal form that opens when clicking an empty div container, where I can then enter an image URL into a form in the modal. That link will then populate the img tag within the div container.
Problem: After the first keystroke, the modal refreshes, and I have to click into the input filed to continue. I can only enter one letter at a time. I don't think this is an issue that requires e.preventDefault, because this is happening before I hit submit. also I've tried using it as a second argument in my onchange method in the chance that it would work.
The issue only occurs after I set the value of the input field is set to state, and the onChange event is included.
This is the error that I received in terminal:
findDOMNode is deprecated in StrictMode. findDOMNode was passed an instance of Transition which is inside StrictMode. Instead, add a ref directly to the element you want to reference. Learn more about using refs safely here: https://reactjs.org/link/strict-mode-find-node
which led me to a question on stack overflow where the scenario was similar, but so far solutions listed haven't worked. But a major take away is that this maybe and issue with changing state causing the modal to re-render. I've began to attempt to implement useRef, but so far have been unsuccessful.
edit: I realized after looking more into useRef that my attempt at it was poor, but I was short on time and trying to type this while learning how to use it.
second edit: After reading a bunch that night and the next morning I have found two solutions, useRef and FormData, the latter approach I have not been successful with yet, but I believe it to work when used correctly. I've updated my code below to include my progress. I have not yet figured out how to update the dom with hooks.
third edit: I successfully updated the database, but the dom only updates after a refresh. Also, i intended to only update the one div, but I updated all 50. I think this is because of my placement of my form. So, I'm trying to learn how to use forwardRef, and useImparetiveHandle so I can update state where it's being stored in the parent component, and pass multiple refs down.
I think it's also important to note that the breakthrough for me was getting access to the useRef data which I confirmed by adding
alert(imageRef.current.value) to a handleSubmit method.
Below is a snippet of my code, where different approaches are included the areas that are commented out
///INITIAL APPROACH
const textInput = useRef(null)
const [url, setUrl] = useState("")
const handleChange = (urlData) => {
// e.preventDefault()
// console.log(e.target.value)
// setUrl(prevUrl => prevUrl)
// setUrl(prevUrl => prevUrl + e.target.value)
setUrl(urlData)
// debugger
// setUrl(url + e.target.value)
}
const ModalForm = () => {
return(
<Modal
// animation={false}
show={openModalForm}
onHide={modalToggle}
url={url}
// data-keyboard="false"
data-backdrop="static"
>
<Modal.Header>
<button
className="modalBtn"
onClick={modalToggle}
>X</button>
</Modal.Header>
<form onSubmit={addPhoto} >
<input
ref={textInput}
type="text"
value={url}
// onChange={console.log(url)}
onChange={(e) => {setUrl(e.target.value)}}
// onChange={handleChange}
// onChange={(e) => handleChange(e)}
// onChange={(e) => handleChange(e, e.target.value)}
/>
</form>
<p></p>
</Modal>)}
/// UPDATED APPROACH
const imageRef = useRef()
const detailRef = useRef()
const [url, setUrl] = useState("")
const [details, setDetails] = useState("")
const handleSubmit = (e) => {
e.preventDefault()
console.log(e)
fetch(`http://localhost:3000/photos/${photo.id}`, {
method: 'PATCH',
headers: {
"Content-Type": "application/json"
},
body: JSON.stringify({
image: imageRef.current.value,
details: detailRef.current.value
})
})
.then(res => res.json())
.then(photoObj =>
console.log(photoObj)
// (updatedBox) => {
// setUrl(photo.image)
// setDetails(photo.details)
// }
)
}
<form
ref={form}
onSubmit={handleSubmit}
>
<input
type="text"
name="image"
placeholder="url"
ref={imageRef}
/>
<input
type="text"
name="details"
placeholder="details"
ref={detailRef}
/>
<button type="submit">ENTER</button>
</form>
I'm a little late, but I resolved the re-rendering issue by moving the modal from within the function and into the components return

Persistent way for getting an Input Value using Hooks

I'm a beginner at React. I'm trying to build a component that recovers the text of an input and later on inserts it on the HTML.
import React, { useState } from 'react';
function InputGetter(){
const [text, setText] = useState("")
let typed = ""
return(
<div>
<input type='text' onChange = {(e)=> typed = e.target.value}></input>
<button onClick={() => setText(typed)}>Set Text</button>
Text typed was "{text}"
</div>
)
}
export default InputGetter
The code above works for clicks when there's a change triggered. However, when I try to double click it, even when there's content on the input, text somehow is set to "". I'm not sure why it happens. How can I make the textvariable content persistent?
You have closure on text value within the onClick callback, to persist value between renders, you can use useRef hook, see Is there something like instance variables?.
function InputGetter() {
const [text, setText] = useState('');
const inputRef = useRef('');
return (
<div>
<input type="text" ref={inputRef}></input>
<button onClick={() => setText(inputRef.current.value)}>Set Text</button>
Text typed was "{text}"
</div>
);
}

Can't recover Checkbox state with onChange method

I'm working with reactjs, trying to say that when the state of the checkbox changes I want to do this or that.
The issue is, I can't recover the checkbox state.
Here is my checkbox component from checkbox.jsx:
const Checkbox = ({ input, label, disabled }) => (
<div>
<Input
{...input}
id={input.name}
checked={input.value}
type="checkbox"
disabled={disabled}
/>
<Label htmlFor={input.name}>{label}</Label>
</div>
)
Here is my code for rendering the checkbox in form.jsx:
<div className="col-md-3">
<Field
name="checkboxName"
type="checkbox"
component={Checkbox}
label="checkbox name"
onChange={changeCheckboxValue(Checkbox.state.value)}
/>
</div>
my code for changeCheckboxValue in Checkbox.jsx (just printing the value passed in parameter):
export const changeCheckboxValue = (value) => {
console.log(value)
}
I precise that the form is not a class but a const.
I'm not a reactjs developer and struggle quite a lot with understanding where this error comes from.
UPDATE:
Thanks to Davo Mkrtchyan, I've updated my code as follow
I added those lines to checkbox.jsx
export const [count, setCount] = useState(false)
export const isChecked = () => true === count
And changed checkbox field rendering in form.jsx to :
<div className="col-md-3">
<Field
name="checkboxname"
type="checkbox"
component={Checkbox}
label="Travailleur handicapé"
onClick={() => setCount(!count)}
/>
</div>
Now I got the following error :
Uncaught Invariant Violation: Invalid hook call. Hooks can only be called inside of the body of a function component. This could happen for one of the following reasons:
1. You might have mismatching versions of React and the renderer (such as React DOM)
2. You might be breaking the Rules of Hooks
3. You might have more than one copy of React in the same app
I understand it might be due to useState (if I delete the line containing it I no longer have this error) my question would be, why ?
If anyone has any hints for me I'd appreciate it.
Thanks
So, if you want to use your Input's state, you need to use useState() hook, something like this:
export const CheckBoxComponent = (props) => {
const [check,setChecked] = useState(false);
const onChange = () => {
setChecked(!check)
};
const handlerFunction = () =>{
if(checked){
//do stuff
} else {
// do other stuff
};
}
return (
<Input
{...input}
id={input.name}
checked={check} // <----- check is current state
type="checkbox"
onChange={onChange} // <----- onChange will update current state
disabled={disabled}
/>)
}
You can look at docs of useState here
I hope this helps, if no, please share codesanbox, or repl, so we can investigate your case.

Resources