Is this an example of 2-way databinding in React using Hooks? - reactjs

I believe it is because it binds the value back to the property inputText but just want to make sure I'm stating this correctly.
import React, { useState } from "react";
const InputElement = () => {
const [inputText, setInputText] = useState("");
return (
<div>
<input
placeholder="Enter Some Text"
onChange={e => {
setInputText(e.target.value);
}}
/>
</div>
);
};
export default InputElement;

This is a good example of 2 way data binding because when you update the state, the UI changes, and when the UI changes, the state changes. Just need to remind you to set the value prop on the <input> element to inputText so that it's a controlled component.
import React, { useState } from "react";
const InputElement = () => {
const [inputText, setInputText] = useState("");
return (
<div>
<input
placeholder="Enter Some Text"
onChange={e => {
setInputText(e.target.value);
}}
value={inputText}
/>
</div>
);
};
export default InputElement;

Related

React function in a component

I am working in React with components, but I am not getting functions working in the Input component.
I have tried searching on component and onChange or onKeyPress but they do not show what I need. Maybe I'm searching wrong? I did get the component Button working, just not Input.
The code for the component is simple.
import React from 'react';
import styles from './Input.module.css';
function Input({inputText}) {
return <input type='number'
placeholder={inputText}
className={styles.input}
/>
}
export default Input;
The full code I want to use for component Input is as follows.
import React, {useState} from 'react';
import styles from './Food.module.css';
import axios from 'axios';
import Nutrients from '../../components/nutrients/Nutrients'
import {ReactComponent as LoadingIcon} from '../../assets/loading.svg'
import Button from "../../components/button/Button";
import Input from "../../components/input/Input";
function Food() {
const [food, setFood] = useState(null);
const [calories, setCalories] = useState('');
const [disabled, setDisabled] = useState(false);
const [error, setError] = useState('');
const [loading, toggleLoading] = useState('');
async function foodData() {
setError('');
toggleLoading('true')
try {
const result = await axios.get(`https://api.spoonacular.com/mealplanner/generate?apiKey=${process.env.REACT_APP_API_KEY}&timeFrame=day&targetCalories=${calories}`);
setFood(result.data);
} catch (error) {
setError('Oops... something went wrong. Please try again.');
console.log('Something went wrong while retrieving the data.', error);
}
toggleLoading(false);
}
function handleChange(e) {
setCalories(e.target.value);
}
function handleKeyPress(e) {
if (e.key === 'Enter') {
if (disabled) {
return;
}
foodData();
setDisabled(true);
}
}
return (
<>
{error && <p className={styles.error}>{error}</p>}
<div>
<section className={styles.food}>
<Input
inputText='Enter caloriessss'
onChange= {handleChange}
onKeyPress={handleKeyPress}
/>
<Button
onClick={() => {
foodData();
setDisabled(true);
}}
buttonText='Click for your daily meals'
/>
</section>
{loading && <LoadingIcon className={styles.loader}/>}
{food && <Nutrients mealsData={food}/>}
</div>
</>
);
}
export default Food;
The thing I want is for the two functions onChange and onKeyPress to work in the Input component.
Anyone has any ideas? I also tried using () and ''.
Because you don't use props onChange and onKeyPress in Input component. Just add it like this:
function Input({ inputText, onChange, onKeyPress }) {
return (
<input
type="number"
placeholder={inputText}
className={styles.input}
onChange={onChange}
onKeyPress={onKeyPress}
/>
);
}
Or shorter way:
function Input({ inputText, ...props }) {
return (
<input
type="number"
placeholder={inputText}
className={styles.input}
{...props}
/>
);
}
React has no way of knowing the onChange and onKeyPress props should be passed to the input inside of your Input component.
You must pass them along explicitly.
// Add them to your destructured props
function Input({inputText, onChange, onKeyPress}) {
return <input type='number'
onChange={onChange} // Pass them to the input
onKeyPress={onKeyPress} // Pass them to the input
placeholder={inputText}
className={styles.input}
/>
}
In the component Input, your return should be: <input></input>
Because JSX needs to closed tags.

React show element if input has value and hide if empty

im trying to show the p element when the input filed has value "the user writes something in input field" and hides when the input is empty
import React, {useState} from 'react'
function textInput() {
const [isOpen, setIsOpen] = useState(false)
return (
<>
<input type="text" onKeyUp={() => setIsOpen(!isOpen)} />
{
isOpen ?
<p>result</p>
: null
}
</>
)
}
export default textInput
Rather than using an isOpen prop, consider maintaining the text in state. Then, if the text is not empty, show the <p> component:
import React, { useState } from "react";
function textInput() {
const [text, setText] = useState("");
return (
<>
<input
type="text"
value={text}
onChange={(e) => {
setText(e.target.value);
}}
/>
{text && <p>result</p>}
</>
);
}
export default textInput;
Please write code like below. It works.
import React, { useState } from "react";
function textInput() {
const [inputText, setInputText] = useState("")
return (
<>
<input type="text" onChange={ (e) => {
setInputText(e.target.value)
}
}/>
{ (inputText !== "") && <p>result: {inputText}</p> }
</>
)
}
export default textInput;

Edit/Update input field with React

I'm new to react and I'm trying to edit an input field after I prefilled its value with an object value from my database, so what should I put on onChange if value is value={data.info}? because I cannot type or change the initial value. I've watched a lot of tutorials but this. and props are very confusing to me
import React, { useState } from 'react';
import { useParams } from 'react-router-dom';
import useAsync from '../useAsync';
export default function Details() {
const url = 'https://..';
const { index } = useParams();
const { data } = useAsync(url + index);
const [state, setState] = useState(false);
const showForm = () => {
return (
<div>
<form>
<input type="text" value={data.info} onChange={} />
</form>
</div>
)
}
return (
<div className="details" >
{data && <p key={index}>{data.info}</p>}
<button onClick={() => setState({ showForm: true })}>Edit</button>
{state.showForm ? showForm() : null}
</div>
)
}
You can add "default value" to your state. So you can move the data value to your useState(false) so useState(data)
import React, { useState } from "react";
const App = () => {
const [formInput, setFormInput] = useState(""); // You can add your "data" as default value
return (
<div className="App">
<h1>Hello CodeSandbox</h1>
<h2>formInput Value {formInput}</h2>
<input
type="text"
value={formInput}
onChange={(e) => setFormInput(e.target.value)} // You need to set the state with the onchange value from the input
/>
</div>
);
};
export default App;
Link to CodeSandbox

useEffect with local variable

I am trying to call useEffect funtion onchange of local variable, but its not working is only works if i use it with useState variable, I know there might be some basic thing here that I am not aware of.
sandbox link: https://codesandbox.io/s/affectionate-gareth-igyv7?file=/src/demo.js
import React, { useEffect, useState } from "react";
import "./styles.css";
export default function Demo() {
const [value, setValue] = useState("");
let valueOne, valueTwo;
const setValueOne = (value) => {
valueOne = value;
};
useEffect(() => {
console.log(value);
console.log(valueOne);
}, [value, valueOne]);
return (
<div>
<h1>Demo</h1>
<input
placeholder="useState"
onChange={(e) => setValue(e.target.value)}
/>
<input
placeholder="function"
onChange={(e) => setValueOne(e.target.value)}
/>
{/* {console.log(valueOne)} */}
</div>
);
}
setValueOne will not rerender your component, If you want to fire a re-render, useEffect function needs to have a useState which basically hold state between re-renders.
You can try managing your state like below, its more readable and it will work too.
import React, { useState } from "react";
import "./styles.css";
export default function Demo() {
const [valueOne, setValueOne] = useState("");
const [valueTwo, setValueTwo] = useState("");
const handleValueOne = (e) => {
setValueOne(e.target.value);
};
const handleValueTwo = (e) => {
setValueTwo(e.target.value);
};
return (
<div>
<h1>Demo</h1>
<input
value={valueOne}
placeholder="useState"
onChange={handleValueOne}
/>
<input
value={valueTwo}
placeholder="function"
onChange={handleValueTwo}
/>
{/* {console.log(valueOne)} */}
</div>
);
}

Custom react hook to for text field seems to be broken

I have been trying to come up with a custom hook to make the textfield configurable, i.e pass the set of data to a custom hook which would give me the text field that needs to be used.
The text field using the hook is being rendered as expected but I do not understand why this approach is breaking the input created using the custom hook. After every keystroke the input is losing focus and is not working as the other input that is using useState directly. It would be great if someone can explain what is going wrong and what I failed to understand.
App.js
import React, { useState } from "react";
import ReactDOM from "react-dom";
import useTextFieldBroken from "./useTextFieldBroken";
import "./styles.css";
function App() {
const [notBrokenValue, notBrokenSetValue] = useState("");
const [TextFieldBrokenInputOne] = useTextFieldBroken(
"brokenOne",
"Broken Input One",
""
);
const notBrokenOnChange = e => {
notBrokenSetValue(e.target.value);
};
return (
<div>
<label htmlFor="notBroken">
<h3>Not Broken Input</h3>
<input
id="notBroken"
onChange={notBrokenOnChange}
value={notBrokenValue}
/>
</label>
<TextFieldBrokenInputOne />
</div>
);
}
const rootElement = document.getElementById("root");
ReactDOM.render(<App />, rootElement);
customHook.js
import React, { useState } from "react";
const useTextFieldBroken = (id, label, initialValue = "") => {
const [value, setValue] = useState(initialValue);
const handleChange = e => {
setValue(e.target.value);
};
const TextField = () => {
console.log("Rendered the input field");
return (
<label htmlFor={id}>
<h3>{label}</h3>
<input
type="text"
name={id}
id={id}
onChange={handleChange}
value={value}
/>
</label>
);
};
return [TextField, value, setValue];
};
export default useTextFieldBroken;
https://codesandbox.io/s/4xj382vj40
Your input is losing focus because you're completely re-rendering the tree that creates it on each change.
The good news is that you don't need a hook to do this, just convert your hook into a functional component instead:
App.js
import React, { useState } from "react";
import ReactDOM from "react-dom";
import TextFieldBroken from "./useTextFieldBroken";
import "./styles.css";
function App() {
const [notBrokenValue, notBrokenSetValue] = useState("");
const notBrokenOnChange = e => {
notBrokenSetValue(e.target.value);
};
return (
<div>
<label htmlFor="notBroken">
<h3>Not Broken Input</h3>
<input
id="notBroken"
onChange={notBrokenOnChange}
value={notBrokenValue}
/>
</label>
<TextFieldBroken label="Previously Broken" id="previously-broken" />
</div>
);
}
const rootElement = document.getElementById("root");
ReactDOM.render(<App />, rootElement);
customHook.js
import React, { useState } from "react";
const TextFieldBroken = ({ id, label, initialValue = "" }) => {
const [value, setValue] = useState(initialValue);
const handleChange = e => {
setValue(e.target.value);
};
return (
<label htmlFor={id}>
<h3>{label}</h3>
<input
type="text"
name={id}
id={id}
onChange={handleChange}
value={value}
/>
</label>
);
};
export default TextFieldBroken;

Resources