Why does state only update after invoking twice using React Hooks? - javascript

I'm stuck trying to understand why my state won't update until I change the value in the text input twice (calling the handleChange function). What am I doing wrong here?
import React, {useEffect, useState} from "react";
export default function Typeahead(props){
const {list} = props;
const [colorList] = useState(list.map(element => element.toLowerCase()));
const [color,setColor] = useState();
const [showResults, setShowResults]= useState(false);
const [results,setResults]= useState();
let handleChange = (e) =>{
setShowResults(true);
setColor(e.target.value.toLowerCase());
const match = (colorList) =>{
return colorList.startsWith(color,0);
};
const matches = colorList.filter(match);
setResults((matches));
console.log(results);
console.log(showResults);
};
useEffect(() => {
//setResults(list.map(elements => elements.toLowerCase()));
}, [results]);
return(
<div>
<input type= "text" onChange={handleChange}/>
{showResults ?
<div>
{results.map((options) => {
return (
<option key={options} value={options}> {options}</option>
)
})}
</div>
: null }
</div>
);
}

Related

make todo and store in localstorage but getting this error fetchdata.map is not a function at App (App.js:27:1)

import React, { useState, useEffect } from 'react';
import './style.css';
export default function App() {
const [state, setState] = useState([]);
const [inputData, setInputData] = useState();
const [fetchdata, setFetchData] = useState([])
const addHandler = () => {
setState((data) => {
return [...data, inputData];
});
localStorage.setItem('state', JSON.stringify(state));
setInputData('');
};
setFetchData(localStorage.getItem('state'))
return (
<div>
<input
onChange={(e) => setInputData(e.target.value)}
value={inputData || ''}
placeholder="add items"
/>
<button onClick={addHandler}>Add</button>
{fetchdata?.map((item) => {
return (
<div style={{ color: `#+${color}` }}>
<li key={item}>{item}</li>
</div>
);
}) || []}
</div>
);
}
This is the code I have tried also need dynamic colors for lists. Any help is appreciated with big thanks
even the key I have given unique but it says unique key required
Try to add a default value to your fetchData:
const [fetchdata, setFetchData] = useState(localStorage.getItem('state') ?? []);
and please don't begin to use useless useEffect like every begginer are doing, further documentation here !
try:
remove
setFetchData(localStorage.getItem('state'))
replace
const initData = () => {
try {
return JSON.parse(localStorage.getItem('state'));
} catch (e) {
return [];
}
}
const [fetchdata, setFetchData] = useState(initData())

How to write value to localStorage and display it in input on reload?

I have an input on the page, initially it is empty. I need to implement the following functionality: on page load, the component App fetches from localStorage a value of key appData and puts it in the input. That is, so that in the localStorage I write the value to the input and when reloading it is displayed in the input. How can i do this?
I need to use useEffect
import { useEffect, useState } from "react";
export default function App() {
const [userData, setUserData] = useState("");
useEffect(() => {
localStorage.setItem("Userdata", JSON.stringify(userData));
}, [userData]);
return (
<div>
<input value={userData} onChange={(e) => setUserData(e.target.value)}></input>
</div>
);
}
Use the change event to write to the localStorage, then use an init function in the useState hook.
import { useState } from 'react';
const loadUserData = () => localStorage.getItem('UserData') || '';
const saveUserData = (userData) => localStorage.setItem('UserData', userData);
export default const Application = () => {
const [ userData, setUserData ] = useState(loadUserData);
const handleUserDataUpdate = e => {
const userData = e.target.value;
setUserData(userData);
saveUserData(userData);
};
return <div>
<label htmlFor="testInput">Test Input</label>
<input id="testInput" value={ userData } onChange={ handleUserDataUpdate } />
</div>;
}
If you need an example using uncontrolled inputs, here is one using useEffect :
import { useEffect } from 'react';
const loadUserData = () => localStorage.getItem('UserData') || '';
const saveUserData = (userData) => localStorage.setItem('UserData', userData);
export default const Application = () => {
const inputRef = useRef();
useEffect(() => {
inputRef.current.value = loadUserData();
}, []); // initial load
const handleUpdateUserData = () => {
saveUserData(inputRef.current.value);
};
return <div>
<label htmlFor="testInput">Test Input</label>
<input ref={ inputRef } id="testInput" onChange={ handleUpdateUserData } />
</div>;
}
You can set a default value for the input inside state.
const [userData, setUserData] =
useState(JSON.parse(localStorage.getItem('Userdata')) || '');
So when the component mounts (after reload), the initial userData value is taken directly from the localStorage. If it's empty, the fallback value will be set ('').
Note: Make sure to add also the onChange handler to the input.

How can I send the state (useState) of one file component to another file's component?

REACT.js:
Let say I have a home page with a search bar, and the search bar is a separate component file i'm calling.
The search bar file contains the useState, set to whatever the user selects. How do I pull that state from the search bar and give it to the original home page that
SearchBar is called in?
The SearchBar Code might look something like this..
import React, { useEffect, useState } from 'react'
import {DropdownButton, Dropdown} from 'react-bootstrap';
import axios from 'axios';
const StateSearch = () =>{
const [states, setStates] = useState([])
const [ stateChoice, setStateChoice] = useState("")
useEffect (()=>{
getStates();
},[])
const getStates = async () => {
let response = await axios.get('/states')
setStates(response.data)
}
const populateDropdown = () => {
return states.map((s)=>{
return (
<Dropdown.Item as="button" value={s.name}>{s.name}</Dropdown.Item>
)
})
}
const handleSubmit = (value) => {
setStateChoice(value);
}
return (
<div>
<DropdownButton
onClick={(e) => handleSubmit(e.target.value)}
id="state-dropdown-menu"
title="States"
>
{populateDropdown()}
</DropdownButton>
</div>
)
}
export default StateSearch;
and the home page looks like this
import React, { useContext, useState } from 'react'
import RenderJson from '../components/RenderJson';
import StateSearch from '../components/StateSearch';
import { AuthContext } from '../providers/AuthProvider';
const Home = () => {
const [stateChoice, setStateChoice] = useState('')
const auth = useContext(AuthContext)
console.log(stateChoice)
return(
<div>
<h1>Welcome!</h1>
<h2> Hey there! Glad to see you. Please login to save a route to your prefered locations, or use the finder below to search for your State</h2>
<StateSearch stateChoice={stateChoice} />
</div>
)
};
export default Home;
As you can see, these are two separate files, how do i send the selection the user makes on the search bar as props to the original home page? (or send the state, either one)
You just need to pass one callback into your child.
Homepage
<StateSearch stateChoice={stateChoice} sendSearchResult={value => {
// Your Selected value
}} />
Search bar
const StateSearch = ({ sendSearchResult }) => {
..... // Remaining Code
const handleSubmit = (value) => {
setStateChoice(value);
sendSearchResult(value);
}
You can lift the state up with function you pass via props.
const Home = () => {
const getChoice = (choice) => {
console.log(choice);
}
return <StateSearch stateChoice={stateChoice} giveChoice={getChoice} />
}
const StateSearch = (props) => {
const handleSubmit = (value) => {
props.giveChoice(value);
}
// Remaining code ...
}
Actually there is no need to have stateChoice state in StateSearch component if you are just sending the value up.
Hello and welcome to StackOverflow. I'd recommend using the below structure for an autocomplete search bar. There should be a stateless autocomplete UI component. It should be wrapped into a container that handles the search logic. And finally, pass the value to its parent when the user selects one.
// import { useState, useEffect } from 'react' --> with babel import
const { useState, useEffect } = React // --> with inline script tag
// Autocomplete.jsx
const Autocomplete = ({ onSearch, searchValue, onSelect, suggestionList }) => {
return (
<div>
<input
placeholder="Search!"
value={searchValue}
onChange={({target: { value }}) => onSearch(value)}
/>
<select
value="DEFAULT"
disabled={!suggestionList.length}
onChange={({target: {value}}) => onSelect(value)}
>
<option value="DEFAULT" disabled>Select!</option>
{suggestionList.map(({ id, value }) => (
<option key={id} value={value}>{value}</option>
))}
</select>
</div>
)
}
// SearchBarContainer.jsx
const SearchBarContainer = ({ onSelect }) => {
const [searchValue, setSearchValue] = useState('')
const [suggestionList, setSuggestionList] = useState([])
useEffect(() => {
if (searchValue) {
// some async logic that fetches suggestions based on the search value
setSuggestionList([
{ id: 1, value: `${searchValue} foo` },
{ id: 2, value: `${searchValue} bar` },
])
}
}, [searchValue, setSuggestionList])
return (
<Autocomplete
onSearch={setSearchValue}
searchValue={searchValue}
onSelect={onSelect}
suggestionList={suggestionList}
/>
)
}
// Home.jsx
const Home = ({ children }) => {
const [result, setResult] = useState('')
return (
<div>
<SearchBarContainer onSelect={setResult} />
result: {result}
</div>
)
}
ReactDOM.render(<Home />, document.getElementById('root'))
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.9.0/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.9.0/umd/react-dom.production.min.js"></script>
<div id="root"></div>
Just pass a setState to component
parent component:
const [state, setState] = useState({
selectedItem: ''
})
<StateSearch state={state} setState={setState} />
change parent state from child component:
const StateSearch = ({ state, setState }) => {
const handleStateChange = (args) => setState({…state, selectedItem:args})
return (...
<button onClick={() => handleStateChange("myItem")}/>
...)
}

Passing data to sibling components with react hooks?

I want to pass a variable username from sibling1 component to sibling2 component and display it there.
Sibling1 component:
const sibling1 = ({ usernameData }) => {
// I want to pass the username value I get from input to sibling2 component
const [username, setUsername] = useState("");
const handleChange = event => {
setUsername(event.target.value);
};
return (
<Form.Input
icon='user'
iconPosition='left'
label='Username'
onChange={handleChange}
/>
<Button content='Login' onClick={handleClick} />
)
}
export default sibling1;
Sibling2 component:
export default function sibling2() {
return (
<h1> Here is where i want to display it </h1>
)
}
You will need to handle your userName in the parent of your siblings. then you can just pass setUsername to your sibling1, and userName to your sibling2. When sibling1 use setUsername, it will update your parent state and re-render your sibling2 (Because the prop is edited).
Here what it looks like :
const App = () => {
const [username, setUsername] = useState('Default username');
return (
<>
<Sibling1 setUsername={setUsername} />
<Sibling2 username={username} />
</>
)
}
const Sibling2 = ({username}) => {
return <h1> Helo {username}</h1>;
}
const Sibling1 = ({setUsername}) => {
return <button onClick={setUsername}>Set username</button>;
}
In parent of these two components create a context where you will store a value and value setter (the best would be from useState). So, it will look like this:
export const Context = React.createContext({ value: null, setValue: () => {} });
export const ParentComponent = () => {
const [value, setValue] = useState(null);
return (
<Context.Provider value={{value, setValue}}>
<Sibling1 />
<Sibling2 />
</Context.Provider>
);
Then in siblings you are using it like this:
const Sibling1 = () => {
const {setValue} = useContext(Context);
const handleChange = event => {
setValue(event.target.value);
};
// rest of code here
}
const Sibling2 = () => {
const {value} = useContext(Context);
return <h1>{value}</h1>;
}
best way: React Context + hooks
you can use React Context. take a look at this example:
https://codesandbox.io/s/react-context-api-example-0ghhy

Can I consolidate multiple functions that set state based on callbacks from React child components?

I'm using React hooks to set state. There is a parent component that has multiple child components. The parent component has the state, and passes functions to the children components to update its state as callbacks.
The child components are the same, they just receive different function callbacks to update the related state in the parent.
My question is, can I write one handleChange function in the parent that will allow me to use this function callback structure to set multiple state values in the parent?
Parent component:
import React, { useState } from 'react'
import Control from './Control'
const Sort = () => {
const [controlUpValues, setControlUpValues] = useState([])
const [controlDownValues, setControlDownValues] = useState([])
const handleControlUpChange = values => {
setControlUpValues(values)
}
const handleControlDownChange = values => {
setControlDownValues(values)
}
return
<>
<Control
setControlItems={handleControlUpChange}
/>
<Control
setControlItems={handleControlDownChange}
/>
</>
)
}
export default Sort
Child component:
import React, { useState } from 'react'
import { Button, TextField } from '#material-ui/core'
function Control({ setControlItems }) {
const [controlInputValues, setControlInputValues] = useState([])
const [inputRef, setInputRef] = useState([])
const [inputValues, setInputValues] = useState([])
const handleValueChange = () => setInputValues(inputRef.value)
const addValuesToItems = () => {
setControlItems(inputValues)
}
return (
<div>
<TextField
inputRef={ref => setInputRef(ref)}
value={controlInputValues ? controlInputValues : ''}
onChange={handleValueChange}
/>
<Button
onClick={addValuesToItems}
>
Add
</Button>
</div>
)
}
export default Control
You can have an object containing the functions to update the state :
Parent
import React, { useState } from 'react'
import Control from './Control'
const Sort = ({ classes }) => {
const [controlBoostValues, setControlBoostValues] = useState([])
const [controlBuryValues, setControlBuryValues] = useState([])
const functions = {
boost: setControlBoostValues,
bury: setControlBuryValues
}
const handleChange = (key, values) => functions[key](values);
return
<>
<Control
setControlItems={handleChange}
/>
<Control
setControlItems={handleChange}
/>
</>
)
}
Child :
import React, { useState } from 'react'
import { Button, TextField } from '#material-ui/core'
function Control({ setControlItems }) {
const [controlInputValues, setControlInputValues] = useState([])
const [inputRef, setInputRef] = useState([])
const [inputValues, setInputValues] = useState([])
const handleValueChange = () => setInputValues(inputRef.value)
const addValuesToItems = () => {
setControlItems("boost" , inputValues)
}
return (
<div>
<TextField
inputRef={ref => setInputRef(ref)}
value={controlInputValues ? controlInputValues : ''}
onChange={handleValueChange}
/>
<Button
onClick={addValuesToItems}
>
Add
</Button>
</div>
)
}
export default Control

Categories

Resources