I have this code:
import React, { useState } from "react";
import "./styles.css";
export default function App() {
const [state, setState] = useState(0);
const add = () => {
setState(state + 1);
localStorage.setItem("data", JSON.stringify([state].push(state)));
};
return (
<div className="App">
<button onClick={add}>click</button>
</div>
);
}
I try to add in localStorage numbers clicking on the button.
At the end i want to get in local storage something like this: [1,2,3,4,5,6], depending how many times user click on button.
Now i don't get the expected value.
Question: How to get what i described?
demo:https://codesandbox.io/s/youthful-grothendieck-dwewm?file=/src/App.js:0-359
What about this?
import React, { useState } from "react";
import "./styles.css";
export default function App() {
const [state, setState] = useState([]);
const add = () => {
const _numbers = [...state];
_numbers.push(_numbers.length + 1);
setState(_numbers);
localStorage.setItem("data", JSON.stringify(_numbers));
};
return (
<div className="App">
<button onClick={add}>click</button>
</div>
);
}
it clones (not nested objects, beware!) the empty array (state)
it adds to the array, the length of the array + 1, so you get 1, 2, 3, 4, 5, etc..
it sets the state with that value and stringifies that value to localstorage
other possibility with prevState in a setState call, does basically the same but uses a reference variable
const [state, setState] = useState([]);
const add = () => {
setState(prevState => {
prevState.push(prevState.length + 1);
localStorage.setItem("data", JSON.stringify(prevState));
return prevState;
});
}
First try getting current stored values from localStorage, push the new state and then set in localStorage with updated array. (First time when value not available in LS, default the value to []).
export default function App2() {
const [state, setState] = useState(0);
const add = () => {
const newState = state + 1;
setState(newState);
// Get the currently stored clicks from local storage
const clicks = JSON.parse(localStorage.getItem("data2")) ?? [];
clicks.push(newState);
localStorage.setItem("data2", JSON.stringify(clicks));
};
return (
<div className="App">
<button onClick={add}>click2</button>
</div>
);
}
First: your state should be const [state, setState] = useState([]);
Second:
Array.push returns the new length property of the object upon which the method was called. You want Array.concat method is used to merge two or more arrays returns the new constructed Array.
So this should work:
import React, { useState } from "react";
import "./styles.css";
export default function App() {
const [state, setState] = useState([]);
const add = () => {
setState(prevState => {
const newState = prevState.concat(prevState.length + 1);
localStorage.setItem("data", JSON.stringify(newState));
return newState;
});
};
return (
<div className="App">
<button onClick={add}>click</button>
</div>
);
}
Related
I'm trying to make app that will take the value of input element and use this value in inc/dec component. I have 2 components and I use props.
Component have input and a set button.
Component have inc/dec buttons and reset button
I'm enter number to first component set it with button. Then I want to inc/dec it in second component and I have NaN error. When I reset it the element take the value of first component and after that inc/dec works well.
first component:
import React, { useState } from "react";
import Sample from "../Sample/Sample";
const SetSample = () => {
const [value, setValue] = useState();
const formChange = (e) => {
e.preventDefault();
}
const handlerChange = () => {
const foo = document.querySelector("input").value * 1;
setValue(foo);
}
return (
<div>
<form onSubmit={formChange}>
<input type="text" />
<button type="submit" onClick={handlerChange}>Set start value</button>
</form>
<p>Start value is: {value || 'Please enter a number'}</p>
<hr />
<Sample start={value} />
</div>
);
}
export default SetSample;
second component:
import React, { useState } from "react";
const Sample = (props) => {
const point = props.start;
const [counter = 0, setCounter] = useState(point);
const decrement = () => {
setCounter(counter - 1);
}
const increment = () => {
setCounter(counter + 1);
}
const reset = () => {
setCounter(point);
}
return (
<div>
<button onClick={decrement}>-</button>
<div>{counter}</div>
<button onClick={increment}>+</button>
<div><button onClick={reset}>reset</button></div>
</div>
);
}
export default Sample;
It seems to me that the problem is in counter and point values.
You first have to import useEffect hook from react, then in the dependency array, you have to enter your point variable.
Nothing interacts with your Sample.jsx after you click Set Start Value.
And when you click the reset button there is some interaction.
Due to that and you are able to see the number only after clicking reset.
import React, { useState , useEffect } from "react";
const Sample = (props) => {
const point = props.start;
const [counter = 0, setCounter] = useState(point);
// edited here
useEffect(()=>{
setCounter(props.start);
},[point]);
const decrement = () => {
setCounter(counter - 1);
}
const increment = () => {
setCounter(counter + 1);
}
const reset = () => {
setCounter(point);
}
return (
<div>
<button onClick={decrement}>-</button>
<div>{counter}</div>
<button onClick={increment}>+</button>
<div><button onClick={reset}>reset</button></div>
</div>
);
}
export default Sample;
In the first render you need to set the value of the input to be 0 just not to run into NaN error in SetSample component:
const [value, setValue] = useState(0);
and in Sample component you need to set the counter if it changes using useEffect and don't set 0 for it because it already has 0 from props.start:
const point = props.start;
const [counter, setCounter] = useState(point);
useEffect(() => {
setCounter(point);
}, [point]);
I'm currently trying to figure out how to pass an array from one useState object to another across two different components. In my first useState I have an array called imagesOriginal, which gets filled with file paths dynamically to various different images like in the following:
[
"https://source.unsplash.com/WLUHO9A_xik/900x900",
"https://source.unsplash.com/R4K8S77qtwI/900x900",
"https://source.unsplash.com/jJGc21mEh8Q/900x900"
]
In my App.js, I construct it like so.
import React, { useCallback, useState } from 'react';
import ShowImage from './ShowImage.jsx';
import DropBox from './DropBox.js';
function App() {
const [imagesOriginal, setImages] = useState([]);
const onDrop = useCallback((acceptedFiles) => {
acceptedFiles.map((file, index) => {
const reader = new FileReader();
reader.onload = function (e) {
setImages((prevState) => [
...prevState,
{ id: index, src: e.target.result },
]);
};
reader.readAsDataURL(file);
return file;
});
}, []);
return (
<div className="App">
<div class="parent">
<div>
<h3>Originals</h3>
<DropBox onDrop={onDrop} />
<ShowImage images={imagesOriginal}/>
</div>
</div>
</div>
);
}
export default App;
The main issue comese in the ShowImage.jsx, where I want to pass that array of images to another useState, as I need to use both the array and the setItems to sort the array with a new order.
import React, { useState } from 'react';
import {
DndContext,
closestCorners,
MouseSensor,
TouchSensor,
DragOverlay,
useSensor,
useSensors,
} from '#dnd-kit/core';
import "./ShowImage.css"
import {arrayMove, SortableContext} from '#dnd-kit/sortable';
import {SortablePhoto} from './SortablePhoto.jsx';
const ShowImage = ({images}) => {
const [items, setItems] = useState([]);
setItems([...images]);
const [activeId, setActiveId] = useState(null);
const sensors = useSensors(useSensor(MouseSensor), useSensor(TouchSensor));
return(
<div class="scroll">
<DndContext
sensors={sensors}
collisionDetection={closestCorners}
onDragStart={handleDragStart}
onDragOver={handleDragOver}
onDragEnd={handleDragEnd}
onDragCancel={handleDragCancel}
>
<SortableContext items={items} strategy={() => {}}>
<div columns={1}
style={{
display: "grid",
gridAutoRows: `100px`,
gridGap: 10
}}
>
{items.map((url, index) => (
<SortablePhoto key={url.src} url={url.src} index={index}/>
))}
</div>
</SortableContext>
</DndContext>
</div>
);
function handleDragStart(event) {
setActiveId(event.active.id);
}
function handleDragOver(event) {
const {active, over} = event;
if (active.id !== over.id) {
setItems((items) => {
const oldIndex = items.indexOf(active.id);
const newIndex = items.indexOf(over.id);
return arrayMove(items, oldIndex, newIndex);
});
}
}
function handleDragEnd(event) {
setActiveId(null);
}
function handleDragCancel() {
setActiveId(null);
}
};
export default ShowImage;
I've tried using the line setItems([...images]); to try and pass the new items in, and also const [items, setItems] = useState(images);, but It never seems to update the items array. I'm probably doing something really stupid, but any help would be greatly appreciated.
You can create a function in your App component that wraps your setItems state modifier and pass this function as a prop to your nested ShowImage component where you could use it to manipulate the state. This way you won't need to maintain 2 different states.
// App.js
function App() {
const [imagesOriginal, setImages] = useState([]);
const setImagesWrapper = useCallback(val => {
setImages(val);
}, [setImages]);
//...
return (
<div className="App">
<div class="parent">
<div>
<h3>Originals</h3>
<DropBox onDrop={onDrop} />
<ShowImage
images={imagesOriginal}
setImages={setImagesWrapper}
/>
</div>
</div>
</div>
);
}
export default App;
// ShowImage.js
const ShowImage = ({ images, setImages }) => {
const [activeId, setActiveId] = useState(null);
const sensors = useSensors(useSensor(MouseSensor), useSensor(TouchSensor));
// ...
};
export default ShowImage;
I want to pass a numbers array like [2,4,5,5,3,7,4,7,0,4] into submitNumbers(), I'm trying to update numbers using an onChange event. it works if I pass the whole array without using the useState(). Trying to console.log these constants while running returns not defined.
function App() {
const [userAccount, setUserAccount] = useState()
const [amount, setAmount] = useState()
const [numbers, setNumbersValue] = useState()
//const numbers = [2,4,5,5,3,7,4,7,0,4]
async function setNumbers() {
if (typeof window.ethereum !== 'undefined') {
await requestAccount()
const provider = new ethers.providers.Web3Provider(window.ethereum);
const signer = provider.getSigner();
const contract = new ethers.Contract(Address, Contract.abi, signer);
const transaction = await contract.submitNumbers(numbers);
await transaction.wait()
//fetchNumbers()
}
}
return (
<button onClick={e => setNumbers()}>Set Numbers</button>
<input onChange={e => setNumbersValue(e.target.value)} placeholder="Numbers" />
);
)
export default App;
Try this for converting your string to array:
import { useEffect, useState } from "react";
export default function App() {
const [state, setState] = useState([]);
const onChange = (e) => {
let value = e.target.value;
setState(value.split(","));
};
useEffect(() => {
console.log(state);
console.log(state);
console.log(typeof state);
}, [state]);
return (
<div className="App">
<input onChange={onChange} />
</div>
);
}
Note: As you said, you should set the input values like: 1,2,3,4,5,6,...
I am trying to call a function from a different component but when I console.log('hi') it appear but it didn't call the messageContext.
Here is my follwing code from Invitees.js:
const [showPreview, setShowPreview] = useState(false);
const toggleUserPreview = () => {
setShowPreview(!showPreview);
};
{showPreview && (
<ResultsWrappers togglePreview={toggleUserPreview}>
<UserPreview
userInfo={applicant}
skillStr={applicant.Skills}
togglePreview={toggleUserPreview}
/>
</ResultsWrappers>
)}
Here is the component have the function I want to call UserPreview.js:
import { useMessageContextProvider } from "../context/MessageContext";
const UserPreview = ({ userInfo, skillStr, togglePreview }) => {
const messageContextProvider = useMessageContextProvider();
const messageUser = () => {
togglePreview();
messageContextProvider.updateActiveUserToMessage(userInfo);
console.log('hi');
};
...
};
Here is my messageContext:
import { createContext, useContext, useState } from "react";
const messageContext = createContext();
export const MessageContextProvider = ({ children }) => {
const [activeUserToMessage, setActiveUserToMessage] = useState({});
const [isOpenMobileChat, toggleMobileChat] = useState(false);
const updateActiveUserToMessage = (user) => {
setActiveUserToMessage(user);
};
return (
<messageContext.Provider
value={{
updateActiveUserToMessage,
activeUserToMessage,
isOpenMobileChat,
toggleMobileChat,
}}
>
{children}
</messageContext.Provider>
);
};
export const useMessageContextProvider = () => {
return useContext(messageContext);
};
When the messageContext called it should open the chatbox like this:
The code you showing is not enough to say it for 100%, but it seems like toggleUserPreview - function called twice, so it reverted to original boolean value.
One time as <ResultsWrappers togglePreview={toggleUserPreview}/>
and second time as <UserPreview togglePreview={toggleUserPreview}/>.
import React, { useState } from "react";
import useInterval from "use-interval";
const useOwnHook = () => {
const arr = [...Array(100)].map((_, index) => index);
return {
arr
};
};
const Component = ({ count }) => {
const { arr } = useOwnHook();
console.log(arr, "arr");
return (
<div className="App">
<h1>{count + 1}</h1>
</div>
);
};
export default function App() {
const [count, setCount] = useState(0);
useInterval(() => {
setCount(count + 1);
}, 1000);
return <Component count={count} />;
}
I've created hook useOwnHook for demonstration that each time Component re-render, each time it goes insite useOwnHook, and create new array, is it possible to prevent it to move inside of this hook each time on re-render?
Personally, I'd use a state variable with a init function:
const useOwnHook = () => {
const [arr] = useState(() => [...Array(100)].map((_, index) => index));
return {
arr
};
};
Benefit of the init function is that it's lazy-evaluated so you won't be constructing the array each time the component is rendered.
You can add useState into your custom hook as:
const useOwnHook = () => {
const [arr] = useState([...Array(100)].map((_, index) => index));
return {
arr
};
};
By doing this you can keep the same array in your useOwnHook.
Also you can import as import { useState } from 'react'.
See also from the Using the State Hook documentation - example with a different variable:
We declare a state variable called count, and set it to 0. React will remember its current value between re-renders, and provide the most recent one to our function. If we want to update the current count, we can call setCount.