Reactjs not calling onChange callback in child - javascript

I have written a re-usable input component for url if a url dont start with http then it will be added http in the beginning.
Here you go for the componet
import React, {useContext, useCallback} from 'react';
const InputURL = ({ name, onChange, ...rest}) => {
const sanitizeURLonChange = React.useCallback((value, actionMeta) => {
if (value.target.value) {
if (!value.target.value.startsWith('http')) {
value.target.value = 'http://' + value.target.value
}
}
}, [onChange])
return (
<>
<input
name={name}
{...rest}
onChange={sanitizeURLonChange}
/>
</>
);
}
export default InputURL;
But when i try to use it in my some component, the onChange doesn't work
I try this way
<InputURL onChange={(e) => console.log(e.target.value)} />
unfortunately the inputURL onChange not working anymore, can you please help me in this case?
I want to achieve. if user input url without http, it will add http,
Like i input it stackoverflow.com/ and then it will return https://stackoverflow.com/ in Onchange

You are closing the bracket right after the event argument : {(e)}. Try like this:
<inputURL onChange={(e, val) => console.log(val)} />
also you have to use the onChange you're passing as props:
const sanitizeURLonChange = (e, actionMeta) => {
let newValue = e.target.value
if (newValue) {
if (!newValue.startsWith('http')) {
newValue = "http://" + newValue
}
}
setVal(newValue);
onChange(event, newValue)
}
but it seems anyway the onChange you are passing as a props to inputURL is not used anywhere so I am not sure what you want to achieve. Also you are calling the component inputURL instead of InputURL and first letter uppercase is very important in JSX.

I think your problem is here:
value.target.value = 'http://' + value.target.value
You are trying to update input value by not using an hook.
Try to rewrite your code in this way:
import React, { useState } from 'react';
const InputURL = ({ name, onChange, ...rest}) => {
const [val, setVal] = useState("");
const sanitizeURLonChange = (value, actionMeta) => {
if (value.target.value) {
if (!value.target.value.startsWith('http')) {
setVal('http://' + value.target.value);
}
else setVal(value.target.value);
}
}
return (
<>
<input
name={name}
{...rest}
value={val}
onChange={sanitizeURLonChange}
/>
</>
);
}
export default InputURL;
Here codesandbox working example.

Related

IonInput not allowing to conditionally prevent the onChange event

On this StackBlitz project: https://stackblitz.com/edit/node-hxolmq?file=src%2Fmain.tsx
I have the following custom control...
/src/controls/IonInputMagic2.js
import { IonInput } from "#ionic/react";
import { useEffect, useState } from "react";
const IonInputMagic2 = props => {
const { value, onChange, validityFunc, ...others } = props
var isValidValue = validityFunc(value);
const initialValue = (typeof value !== 'undefined' && isValidValue) ? value : '';
const [ currentValue, setCurrentValue ] = useState(initialValue);
useEffect(() => {
setCurrentValue(initialValue);
}, [initialValue]);
const handleChange = (e) => {
var value = e.target.value;
if (!validityFunc(value)) {
e.preventDefault();
return false;
}
setCurrentValue(value);
if (onChange) {
onChange(e);
}
};
return (
<IonInput value={currentValue} onChange={handleChange} {...others} />
);
}
export default IonInputMagic2;
where you can see I use the Ionic control: IonInput.
My problem is: I have a validityFunc(...) that decides if what the user enters is acceptable or not. As per that function, only numeric and even digits are allowed. However, the user can enter whatever character with no restrictions.
I have a similar control: IonInputMagic1 which is very similar, but it uses the HTML built-in element: <input /> instead of the Ionic control: <IonInput />. On that control the user can only enter what is expected: only numeric and even digits.
Here is the difference between those 2 controls (left: works | right: doesn't work)
Here is how I use both controls:
What I need is: To make IonInputMagic2 (which uses: IonInput) work as: IonInputMagic1 where the user can only enter numeric and even digits. This is because the IonInput uses all the styling and scripting of Ionic and I don't want to break all that by using: <input />.
Note: I have detected through the DOM that the IonInput is a wrapper of: <input />.
Any idea on how to achieve that?
If possible, please fork the StackBlitz above and post the link here.
Thanks!
This change did the trick:
Here the full code for the component:
import { IonInput } from "#ionic/react";
import { useEffect, useState } from "react";
const IonInputMagic2 = props => {
const { value, onChange, validityFunc, ...others } = props
var isValidValue = validityFunc(value);
const initialValue = (typeof value !== 'undefined' && isValidValue) ? value : '';
const [currentValue, setCurrentValue] = useState(initialValue);
useEffect(() => {
setCurrentValue(initialValue);
}, [initialValue]);
const handleChange = (e) => {
var value = e.target.value;
if (!validityFunc(value)) {
e.preventDefault();
e.target.value = currentValue;
return false;
}
setCurrentValue(value);
if (onChange) {
onChange(e);
}
};
return (
<IonInput value={currentValue} onIonInput={handleChange} {...others} />
);
}
export default IonInputMagic2;

How to change checkbox state for one element instead of all

I am trying to change the state of a checkbox when I have two, but all checkboxes are being checked at the same time, I tried different solutions for 5 days and still nothing ... I'm quite new to react so I'm lost.
import React, { ChangeEvent, useCallback, useState } from 'react';
import ReactDOM from 'react-dom';
import { Checkbox, Pane } from 'evergreen-ui';
function ControlledCheckboxExample() {
const [checkedItems, setCheckedItems] = React.useState(false)
const handleButtonClick = (e) => {
console.log(!checkedItems, e);
setCheckedItems(!checkedItems);
};
return (
<Pane>
<Checkbox
label="Controlled usage"
name="afaf"
key={1}
checked={checkedItems}
onChange={handleButtonClick.bind(name, 1)}
/>
<Checkbox
label="Controlled usage"
name="afatrf"
key={2}
checked={checkedItems}
onChange={handleButtonClick.bind(name, 2)}
/>
</Pane>
)
}
ReactDOM.render(
<ControlledCheckboxExample />,
document.getElementById("root")
)
This is my code, is there any solution you can propose?
Issue
The code is using and updating a single state for all checkbox inputs.
Solution
Convert the checkedItems to an object of booleans and use the onChange event object and the input name to toggle a specific input.
Example:
function ControlledCheckboxExample() {
const [checkedItems, setCheckedItems] = React.useState({});
const handleButtonClick = (e) => {
const { name } = e.target;
setCheckedItems(checkedItems => ({
...checkedItems,
[name]: !checkedItems[name]
}));
};
return (
<Pane>
<Checkbox
label="Controlled usage"
name="afaf"
key={1}
checked={checkedItems["afaf"]}
onChange={handleButtonClick}
/>
<Checkbox
label="Controlled usage"
name="afatrf"
key={2}
checked={checkedItems["afatrf"]}
onChange={handleButtonClick}
/>
</Pane>
);
}
You are using same state variable for both checkboxes and of course if you click on one the second will be set too.
Create another state variable for another checkbox or use an array like so
const [state, setState] = React.useState(new Array({length of how much boxes you have}).fill(false);
and then update state
const handleOnChange = (position) => {
const updatedCheckedState = checkedState.map((item, index) =>
index === position ? !item : item
);
setCheckedState(updatedCheckedState);
}

React, pass a reference created at the parent to the children

I am refactoring my code and I have some logic in the parent that needs to evaluate the value of all the inputs its children have. For that, I am creating 4 references in the parent, and passing them as prop to its children. Like follows:
// References (will be used in multiple functions)
usernameInput = createRef(null);
emailInput = createRef(null);
passwordInput = createRef(null);
repeatPasswordInput = createRef(null);
...
render() {
return (
<View>
<Form1 usernameInputRef={usernameInput} emailInputRef={emailInput} />
<Form2 passwordInputRef={passwordInput} repeatPasswordInputRef={repeatPasswordInput} />
</View>
);
}
And in each child, I am doing this:
// This is Child1. For Child2 gonna be the same but with its props.
const {
usernameInputRef,
emailInputRef,
} = props;
return (
<>
<TextInput
ref={usernameInputRef}
...
/>
<TextInput
ref={emailInputRef}
/>
</>
);
The problem comes when I try to access the value of each child node in the parent... If I do this:
const username = this.usernameInput.current.props.value; // <--- Works if the input is in the same component, and not in the child.
console.log(username);
I get "null".
Any ideas? This was working before refactoring my code into multiple components...
UPDATE
TextInput code:
import React from "react";
import { View, StyleSheet } from "react-native";
import { TextInput as RNPTextInput, useTheme } from "react-native-paper";
const TextInput = forwardRef((props, ref) => {
const { colors } = useTheme();
let {
placeholder,
multiline,
maxLength,
secureTextEntry,
color,
icon,
counter,
onChange,
onSubmit,
onFocus,
containerStyle,
} = props;
...
return (
<>
<View style={containerStyle || styles.inputContainer}>
<RNPTextInput
ref={ref}
...
There is an elegant solution for accessing data from a child. Just combine forwardRef with the useImperativeHandle hook.
Do this:
const TextInput = forwardRef((props, ref) => {
useImperativeHandle(ref, () => ({
getText() {
return text;
},
}));
And instead of acessing the text with this:
const username = this.usernameInput.current.props.value
You will be able to get it with this:
const username = this.usernameInput.current.getText();
Here is a full example: https://medium.com/#nugen/react-hooks-calling-child-component-function-from-parent-component-4ea249d00740

How to access the latest state value in the functional component in React

import React, { useState } from "react";
import Child from "./Child";
import "./styles.css";
export default function App() {
let [state, setState] = useState({
value: ""
});
let handleChange = input => {
setState(prevValue => {
return { value: input };
});
console.log(state.value);
};
return (
<div className="App">
<h1>{state.value}</h1>
<Child handleChange={handleChange} value={state.value} />
</div>
);
}
import React from "react";
function Child(props) {
return (
<input
type="text"
placeholder="type..."
onChange={e => {
let newValue = e.target.value;
props.handleChange(newValue);
}}
value={props.value}
/>
);
}
export default Child;
Here I am passing the data from the input field to the parent component. However, while displaying it on the page with the h1 tag, I am able to see the latest state. But while using console.log() the output is the previous state. How do I solve this in the functional React component?
React state updates are asynchronous, i.e. queued up for the next render, so the log is displaying the state value from the current render cycle. You can use an effect to log the value when it updates. This way you log the same state.value as is being rendered, in the same render cycle.
export default function App() {
const [state, setState] = useState({
value: ""
});
useEffect(() => {
console.log(state.value);
}, [state.value]);
let handleChange = input => {
setState(prevValue => {
return { value: input };
});
};
return (
<div className="App">
<h1>{state.value}</h1>
<Child handleChange={handleChange} value={state.value} />
</div>
);
}
Two solution for you:
- use input value in the handleChange function
let handleChange = input => {
setState(prevValue => {
return { value: input };
});
console.log(state.value);
};
use a useEffect on the state
useEffect(()=>{
console.log(state.value)
},[state])
Maybe it is helpful for others I found this way...
I want all updated projects in my state as soon as I added them
so that I use use effect hook like this.
useEffect(() => {
[temp_variable] = projects //projects get from useSelector
let newFormValues = {...data}; //data from useState
newFormValues.Projects = pro; //update my data object
setData(newFormValues); //set data using useState
},[projects])

React Hooks state not updating

I am unable to update my react hooks state.
So this is what I am doing (this is a minified relevant code).
export const Signup = (props) => {
const key= 'randomKey'
const onTextChangeHandler = (text) => {
console.log(key)
setPayloadData[key] = text
console.log(payload)
}
const [payload, setPayloadData] = useState({})
return (
<View>
<TextInput
placeholder={placeHolder}
number={number}
style={[{color: defaultColor, borderColor: defaultColor}, styles.defaultTextInputStyle, templateStyle]}
onChangeText={text => onTextChangeHandler(text)}
value={payload[key]}
/>
</View>
)
}
here, In the above code, notice
const onTextChangeHandler = (text) => {
console.log(key)
setPayloadData[key] = text
console.log(payload)
}
Here text is coming out to be whatever I typed. console.log of the key is returning the randomKey but
console.log(payload)
Is coming out to be undefined. Can anyone help me in figuring out what I am doing wrong?
setPayload is a function, not an object. What you are actually doing is assigning a new field to the function, the payload remains unchanged, since the function responsible for updating it is not being called.
setPayloadData[key] = text; // the function object mutation occures
Solution: simply invoke it as a function and pass the argument you want:
setPayloadData({ [key]: text });
Example: Update state using useState hook
Update props value and HOC components accordingly
import React, { useState } from 'react';
const Signup = (props) => {
const key= 'userKeyboardStrokes'
const onTextChangeHandler = (event) => {
setPayloadData({ [key]: event.target.value })
}
const [payload, setPayloadData] = useState({})
return (
<React.Fragment>
<input
type="text"
onChange={text => onTextChangeHandler(text)}
/>
</React.Fragment>
)
}
module.exports = Signup;
Output Result:
{
userKeyboardStrokes: "user input on object"
}
Playground Example:
https://stackblitz.com/edit/react-npytvn?file=testing.js
setPayloadData is a setter, it should be setPayloadData(newData) to update the state.

Categories

Resources