useState doesn't change the state in React - reactjs

[Mycode] (https://codesandbox.io/s/romantic-kowalevski-fp00l?file=/src/App.js)
I'm practicing React by making todo-list app.
I want my input empty when i hit Enter. but it didn't work.
here is my whole code :
const Todo = ({ text }) => {
return (
<div>
<span>{text}</span>
</div>
);
};
const InputText = ({ addTodo }) => {
const [txt, setTxt] = useState("");
const handleSubmit = (e) => {
e.preventDefault();
if (!txt) return;
addTodo(txt);
setTxt("");
};
return (
<form onSubmit={handleSubmit}>
<input type="text" onChange={(e) => setTxt(e.target.value)}></input>
</form>
);
};
function App() {
const [todos, setTodos] = useState([]);
const addTodo = (text) => {
const newTodos = [...todos, text];
setTodos(newTodos);
};
return (
<>
<div className="todo-list">
{todos.map((val, idx) => {
return <Todo key={val + idx} text={val} />;
})}
<InputText addTodo={addTodo} />
</div>
</>
);
}
line 17 on the link, setTxt(""); doesn't change state of txt.
how can i fix it?

That is not a "controlled" component since you are not using the value property on the input.
Try
<input type="text" onChange={e => setTxt(e.target.value)} value={txt} />
https://reactjs.org/docs/forms.html

You actually need to set the input value to your state.
Try something like
<Input type="text" onChange={(e) => setTxt(e.target.value)} value={txt}/>
I hope it helps.

Related

How can I send the data to the parent component by click on the button in React?

My question is how can I send the input value to the parent component by clicking on the button? Because now if I type something in the input it shanges the value instantly, I want it to do after I click on the button.
Currently I am using that method:
const FormInput = ({setIpAddress}) => {
return (
<div className="formInput">
<form className="form_container" onSubmit={e => {e.preventDefault();}}>
<input type="text" id="input" onChange={(e) => setIpAddress(e.target.value)} required={true} placeholder="Search for any IP address or domain"/>
<button type="submit" className="input_btn">
<img src={arrow} alt="arrow"/>
</button>
</form>
</div>
);
};
export default FormInput
You can pass an onClick callback function to the child component. When this function is called it will trigger a rerender in the child.
Example:
Parent:
const handleClick = (value) => {
//set the state here
}
<ChildComponent onClick={handleClick} />
Child:
<button type="submit" className="input_btn" onClick={(value) => props.onClick?.(value)}>
In your case you need to get rid of the onChange in your input tag:
parents:
function App() {
const [ipAddress, setIpAddress] = useState("");
const url = `${BASE_URL}apiKey=${process.env.REACT_APP_API_KEY}&ipAddress=${ipAddress}`;
useEffect(() => {
try {
const getData = async () => {
axios.get(url).then((respone) => {
setIpAddress(respone.data.ip);
});
};
getData();
} catch (error) {
console.trace(error);
}
}, [url]);
const handleClick = (event) => {
setIpAddress(event.target.value)
}
return (
<div className="App">
<SearchSection onClick={handleClick} />
</div>
);
}
const SearchSection = ({onClick}) => {
return (
<div className="search_container">
<h1 className="search_heading">IP Address Tracker</h1>
<FormInput onClick={onClick}/>
</div>
);
};
Child
const FormInput = ({onClick}) => {
return (
<div className="formInput">
<form className="form_container" onSubmit={e => {e.preventDefault();}}>
<input type="text" id="input" required={true} placeholder="Search for any IP address or domain"/>
<button type="submit" className="input_btn" onClick={(e) => onClick(e}>
<img src={arrow} alt="arrow"/>
</button>
</form>
</div>
);
};
Thank you for your answer, but I don't really get it, bcs my parent component has no paramter, sorry I am new in react.
This is my parent component where I am fetching the data and I want to update the ipAdress when I click on the button which is in the FormInput component. So the SearchSection is the parent of the FormInput.
function App() {
const [ipAddress, setIpAddress] = useState("");
const url = `${BASE_URL}apiKey=${process.env.REACT_APP_API_KEY}&ipAddress=${ipAddress}`;
useEffect(() => {
const getData = async () => {
axios.get(url).then((respone) => {
setIpAddress(respone.data.ip)
...
getData();
}, [url]);
return (
<div className="App">
<SearchSection setIpAddress={setIpAddress} />
</div>
);
}
I hope it's enough :)
const SearchSection = ({setIpAddress}) => {
return (
<div className="search_container">
<h1 className="search_heading">IP Address Tracker</h1>
<FormInput setIpAddress={setIpAddress}/>
</div>
);
};
function App() {
const [ipAddress, setIpAddress] = useState("");
const url = `${BASE_URL}apiKey=${process.env.REACT_APP_API_KEY}&ipAddress=${ipAddress}`;
useEffect(() => {
try {
const getData = async () => {
axios.get(url).then((respone) => {
setIpAddress(respone.data.ip);
});
};
getData();
} catch (error) {
console.trace(error);
}
}, [url]);
return (
<div className="App">
<SearchSection setIpAddress={setIpAddress} />
</div>
);
}

Passing data between two components in React.js

Currently learning React and building a side project where i can render rss-feeds in my browser window. It works in a single component.
Original working component
function App (){
const [rssUrl, setRssUrl] = useState('');
const [items, setItems] = useState([]);
const getRss = async (e) => {
e.preventDefault();
const urlRegex =
/(http|ftp|https):\/\/[\w-]+(\.[\w-]+)+([\w.,#?^=%&:\/~+#-]*[\w#?^=%&\/~+#-])?/;
if (!urlRegex.test(rssUrl)) {
return;
}
const res = await fetch(`https://api.allorigins.win/get?url=${rssUrl}`);
const { contents } = await res.json();
const feed = new window.DOMParser().parseFromString(contents, 'text/xml');
const items = feed.querySelectorAll('item');
const feedItems = [...items].map((el) => ({
link: el.querySelector('link').innerHTML,
title: el.querySelector('title').innerHTML,
author: el.querySelector('author').innerHTML,
}));
setItems(feedItems);
};
}
return (
<div className="App">
<form onSubmit={getRss}>
<div>
<h1>Next Pod For Chrome</h1>
<label> rss url</label>
<br />
<input onChange={(e) => setRssUrl(e.target.value)} value={rssUrl} />
</div>
<input type="submit" />
</form>
{items.map((item) => {
return (
<div>
<h1>{item.title}</h1>
<p>{item.author}</p>
<a href={item.link}>{item.link}</a>
</div>
);
})}
</div>
);
}
export default App;
At the moment I try to separate the functionality into two components. How can I pass a link from one component to another one where I want to trigger a function handled by the first component?
Any tips are much appreciated. Thanks.
Current state of component to search for rss-feed
function Search() {
const [rssUrl, setRssUrl] = useState('');
const formatRss = async (e) => {
e.preventDefault();
const urlRegex =
/(http|ftp|https):\/\/[\w-]+(\.[\w-]+)+([\w.,#?^=%&:\/~+#-]*[\w#?^=%&\/~+#-])?/;
if (!urlRegex.test(rssUrl)) {
return;
}
console.log(rssUrl);
};
return (
<div className="App">
<form onSubmit={formatRss}>
<div>
<h1>Next Pod For Chrome</h1>
<label>rss url</label>
<br />
<input onChange={(e) => setRssUrl(e.target.value)} value={rssUrl} />
</div>
<input type="Submit" />
</form>
</div>
);
}
export default Search;
Current stage of component to parse and render
function List(props) {
const [items, setItems] = useState([]);
const formatRss = async (e) => {
e.preventDefault();
console.log(rssUrl);
const res = await fetch(`https://api.allorigins.win/get?url=${rssUrl}`);
const { contents } = await res.json();
const feed = new window.DOMParser().parseFromString(contents, 'text/xml');
const items = feed.querySelectorAll('item');
const feedItems = [...items].map((el) => ({
link: el.querySelector('link').innerHTML,
title: el.querySelector('title').innerHTML,
author: el.querySelector('author').innerHTML,
}));
setItems(feedItems);
};
return (
<div className="App">
{items.map((item) => {
return (
<div>
<h1>{item.title}</h1>
<p>{item.author}</p>
<a href={item.link}>{item.link}</a>
</div>
);
})}
</div>
);
}
export default List;
You can declare the state on both's parent, for example: App.js
And use prop to pass the variable to the component
like this:
export default function App() {
const [rssUrl, setRssUrl] = useState("");
return (
<div className="App">
<Search rssUrl={rssUrl} setRssUrl={setRssUrl} />
<List rssUrl={rssUrl} />
</div>
);
}
Below is the live example for you:
https://codesandbox.io/s/cocky-tharp-7d5uu8?file=/src/App.js
There are many platforms where you can put the demo project which make it easier for people to answer your question.

How to add items to array in react

Code:
export default function App() {
const [name,setName] = useState("");
var myArray = [];
const handleAdd = () => {
myArray = [...myArray,name]
setName("")
}
return (
<div className="App">
<input placeholder="type a name" onChange={(e) => setName(e.target.value)}/>
<button onClick={handleAdd}>add</button>
<button onClick={() => console.log(myArray)}>test</button>
{myArray.map((n) => {
return <h2>{n}</h2>
})}
</div>
);
}
OnClick it isn't adding the name to the array.
this is how you "push" to an array with useState
const [array, setArray] = useState([])
setArray(previous => [...previuous, newItem])
You should use a state for your array and set that state to see the changes reflected:
export default function App() {
const [name, setName] = useState('');
const [myArray, setMyArray] = useState([]);
const handleAdd = () => {
setMyArray([...myArray, name]);
setName('');
};
return (
<div className="App">
<input
placeholder="type a name"
onChange={(e) => setName(e.target.value)}
/>
<button onClick={handleAdd}>add</button>
<button onClick={() => console.log(myArray)}>test</button>
{myArray.map((n) => {
return <h2>{n}</h2>;
})}
</div>
);
}
We can also set the state of myArr to be an empty array initially, making it easier to manipulate the subsequent state of that array. The onClick event handler does not fire the handleAdd function, for some reason, it only resets the form and does not provide any state. To submit the form and materialize the state, we can also use the onSubmit event handler instead of onClick. In the same way, we can use the name state as a value/prop for the name input, which will be used by the onChange handler.
import React, { useState } from 'react'
const App = () => {
const [name, setName] = useState('')
const [myArr, setMyArr] = useState([])
const submit = (event) => {
event.preventDefault()
setMyArr(myArr.concat(name))
setName('')
}
//console.log(myArr)
return (
<div className="App">
<form onSubmit={submit}>
<div>
<label htmlFor="name">Name</label>
<input
placeholder="type a name"
type="text"
value={name}
onChange={({ target }) => setName(target.value)}
/>
</div>
<div>
<button type="submit">Add</button>
</div>
</form>
<div>
{myArr.map((arr, index) => (
<div key={index}>
<p>{arr}</p>
</div>
))}
</div>
</div>
)
}
export default App
I have a proclivity of inserting items on an array using concat.
import React, { useState } from 'react'
// ...
const App = () => {
// ...
const [myArr, setMyArr] = useState([])
// somewhere on your event handler e.g. Submit handler
setMyArr(myArr.concat(name))
// ...
}

I'm trying to manipulate the DOM by inserting 2 values

/////////index.ts///////
const Front =() =>{
const [inputs, setInputs] = useState({});
};
const addinputs = useCallback((event) => {
const name = event.target.name;
const value = event.target.value;
setInputs(values => (
{...values, [name]: value}))
},[inputs]);
return (
<>
<h1 id="header1">write you Thoughts</h1>
<label htmlFor='txtname'>Ente your name</label><br></br>
<input name="name" onChange={addinputs} value={inputs.name || ""} type="Text" id="txtname" ></input><br></br>
<label htmlFor="txtname">What are you thinking of</label><br></br>
<input name="thought" onChange={addinputs} value={inputs.thought || ""} id="txt1" type="Text"/><br></br>
<Thoughts addThought={addinputs}></Thoughts>
<hr/>
</>
)
/////////thoughts.ts///////
import {memo} from 'react'
const Thoughts = ({inputs ,addThought}) =>
{
console.log(inputs);
return(
<>
<h1>new thought</h1>
{inputs?.map((input,index) =>
{
return <p key={index}>{input}</p>
})}
<button onClick={addThought} type='button'>Add thought</button>
</>
);
};
export default memo(Thoughts);
but in console.log(inputs) I get undefined ,someone have a clue why?
inputs is a state inside the index.ts.You need to pass the inputs to Thoughts component
<Thoughts addThought={addinputs} inputs={inputs}></Thoughts>

Input not re-rendering onChange with hooks

When typing and logging the input e.target.value, I get the default value + the last key stroke, but nothing re-renders. I guess that React doesn't recognize that the state changed, but I'm having a problem finding out the correct way to do this.
This is the code in question:
const [text, setText] = useState(task.text);
console.log(text);
const handleInputChange = (e) => {
setText(e.target.value);
};
const taskInput = (
<form>
<input type='text' value={text} onChange={handleInputChange} />
</form>
);
And the full file:
import React, { useContext, useState } from "react";
import { TaskContext } from "../context/TaskState";
const Task = ({ task }) => {
const { deleteTask } = useContext(TaskContext);
const { changeStatus } = useContext(TaskContext);
const taskText = (
<div
className='task-text'
onClick={() => changeStatus({ ...task, done: !task.done })}
style={task.done ? { textDecoration: "line-through" } : null}
>
{task.text}
</div>
);
const [text, setText] = useState(task.text);
console.log(text);
const handleInputChange = (e) => {
setText(e.target.value);
};
const taskInput = (
<form>
<input type='text' value={text} onChange={handleInputChange} />
</form>
);
const [option, setOption] = useState(taskText);
return (
<div className='task-container'>
<button className='task-edit' onClick={() => setOption(taskInput)}>
edit
</button>
<button className='task-delete' onClick={() => deleteTask(task.id)}>
x
</button>
{option}
</div>
);
};
export default Task;
I'am using global state for the rest of the app and reducers.
I think, onChange in your input might cause this error. Try replacing this:
onChange={handleInputChange}
with this:
onChange={(e) => handleInputChange(e)}
e object might be not passed to your method.
Please try wrapping your taskInput value in useMemo with dependency text as when you store JSX as variable during re-render they are refering to the previous value as they don't know the variable they used have value changed.
import React, { useMemo, useContext, useState } from "react";
const taskInput = useMemo(() => (
<form>
<input type='text' value={text} onChange={handleInputChange} />
</form>
), [text]);
The problem was the way I passed option inside the jsx.
I made the option state a boolean, converted taskText and taskInput to functions and passed option conditionally inside the jsx.
import React, { useContext, useState } from "react";
import { TaskContext } from "../context/TaskState";
const Task = ({ task }) => {
const { deleteTask } = useContext(TaskContext);
const { changeStatus } = useContext(TaskContext);
const taskText = () => {
return (
<div
className='task-text'
onClick={() => changeStatus({ ...task, done: !task.done })}
style={task.done ? { textDecoration: "line-through" } : null}
>
{task.text}
</div>
);
};
const [text, setText] = useState(task.text);
console.log(text);
const handleInputChange = (e) => {
setText(e.target.value);
};
const taskInput = () => {
return (
<form>
<input type='text' value={text} onChange={handleInputChange} />
</form>
);
};
const [option, setOption] = useState(true);
return (
<div className='task-container'>
<button className='task-edit' onClick={() => setOption(!option)}>
edit
</button>
<button className='task-delete' onClick={() => deleteTask(task.id)}>
x
</button>
{option ? taskText() : taskInput()}
</div>
);
};
export default Task;

Resources