React.Js Taking User's input and Pushing Into An Array - reactjs

Simply, I am trying to take the user's input and push them into an array. The script itself starts with clicking a button, typing a text input, and clicking to repeat the process. How would I be able to get each input from the user? Would it be possible while keeping my Array.from(Array...) or do I try something else.
import React, {useState} from 'react';
import '../Mentor/PageLayout.css'
var mainArray = ["one", "two"];
function MentorQuiz() {
const [counter, setCounter] = useState(0);
const [InputDATA, setInputData] = useState("");
const handleString = (text) => {
let InputDATA = text;
setInputData(InputDATA);
mainArray.push(InputDATA.toString());
console.log(mainArray);
}
const handleClick = () => {
setCounter(counter + 1);
console.log(counter);
};
return (
<div className="App">
<button onClick={() => {
handleString("Hi");
handleClick();
}}> Hello</button>
{Array.from(Array(counter)).map((c, index) => {
return <input key={c} type="text"></input>;
})}
</div>
);
}
export default MentorQuiz;
// I have tested with the OnClick fucntion in my return with handleString("Hi") and I get a //console.log of "Hi". But how do I try with my array?

I think you can do like this:
<input onChange={(e) => setInputData(e.target.value)} />
<button onClick={addUserName}>User Info Entry</button>
For addUserName method:
const addUserName = () => {
if(inputDATA !== ""){
mainArray.push(inputDATA);
// or using spread operator
// mainArray = [...mainArray, inputDATA];
setInputData("")
return;
}
alert("Empty Field")
}

Related

how can I update a nested object state so that is renders the new state after I click it?

YOu can find this code and the data here: git clone https://github.com/Cerezze/ChallengesDestrib2
This program displays a static array of 7 elements. When clicking the button unlock, I want to make it so the taskName appears next to unlock right after the click. But Im not sure why my prevState does not work. You can find the problem at the bottom of getOneRandomTask I think I am setting the user state wrong. But How can I setUser() so that my state updates immediatly after the click? It is a nested object and array so its very confusing I was hoping for some insight. Thanks in advance for all advice.
import { challengeBucket } from "./DB/challengeBucket";
import { user } from "./DB/user";
import { useEffect, useState } from "react";
function App() {
const [u, setUser] = useState({});
const [cb, setChallengeBucket] = useState([]);
const [dayTasks, setDayTasks] = useState([]);
const [update, setUpdate] = useState(false);
useEffect(()=>{
if(localStorage.getItem('user') !== null && localStorage.getItem('challengeBucket') !== null){
setUser({...JSON.parse(localStorage.getItem('user'))});
setChallengeBucket([...JSON.parse(localStorage.getItem('challengeBucket'))]);
setDayTasks([...JSON.parse(localStorage.getItem('user')).challengeOneDays.dayTasks]);
}
},[update]);
const getOneRandomTask = (i) =>{
let chalBuck = cb;
let randomNumber = Math.floor(Math.random() * 6);
let user = u;
let filteredArr = chalBuck.filter((i, index)=>{
return index === randomNumber;
});
user.challengeOneDays.dayTasks[i].taskName = filteredArr[0].taskName;
localStorage.setItem('user', JSON.stringify(user));
setUser(prevState => {
return {...prevState}}); // here is where I am most likely setting state wrong
}
const setMockStorage = () =>{
localStorage.setItem('challengeBucket', JSON.stringify(challengeBucket));
localStorage.setItem('user', JSON.stringify(user));
}
const deleteMockStorage = () =>{
localStorage.removeItem('challengeBucket');
localStorage.removeItem('user');
}
return (
<div>
<button onClick = {setMockStorage}>Set Storage</button>
<button onClick = {deleteMockStorage}>Delete Storage</button>
{dayTasks.map(i => {
return (
<div key={Math.random()}>
<span>
Day {i.day}
</span>
<button onClick={()=>getOneRandomTask(i.day - 1)} disabled = {i.lockStatus === "locked"?true: false}>
{i.lockStatus}
</button>
<span>
{i.taskName}
</span>
</div>
)
})}
</div>
);
}
export default App;

Import function from another page

I need to call function resetToken() from another page when i click on button.
resetToken() should change useState to generate new code. I don't know how to import this function to another page and use it.
I have import
import Captcha from '../../../components/Captcha/Captcha'; and displayed with <Captcha/> in return( ... )
So when i click on button I need to call function resetToken() to generate new code or call again import because I have in <Captcha/>
React.useEffect(() => {
resetToken();
},[]);
This code is Captcha.jsx
import React from 'react';
import './Captcha.css';
function Captcha({statusOfCaptcha}){
const [status, setStatus] = React.useState(undefined);
const [code, setCode] = React.useState(undefined);
const [text, setText] = React.useState("");
const [seconds, setSeconds] = React.useState(120);
function resetToken(){
//generate code
var codeGenerated = "";
var possible = "ABCDEFGHJKLMNPQRSTUVWXYZabcdefghjkmnpqrstuvwxyz123456789";
for (var i = 0; i < 6; i++){
codeGenerated += possible.charAt(Math.floor(Math.random() * possible.length));
}
setCode(codeGenerated);
//reset every 120 second
setInterval(function(){
var codeGenerated = "";
var possible = "ABCDEFGHJKLMNPQRSTUVWXYZabcdefghjkmnpqrstuvwxyz123456789";
for (var i = 0; i < 6; i++){
codeGenerated += possible.charAt(Math.floor(Math.random() * possible.length));
}
setCode(codeGenerated);
setSeconds(120);
setStatus(undefined);
setText("");
}, 120000);
const interval = setInterval(() => {
setSeconds(seconds => seconds - 1);
}, 1000);
return () => clearInterval(interval);
}
React.useEffect(() => {
resetToken();
},[]);
function checkCaptcha(e){
if(e === code){
setStatus(true);
statusOfCaptcha(true);
} else{
setStatus(false);
statusOfCaptcha(false);
}
}
return (
<div className='captcha'>
<div className="background">
<p onCopy={(e) => e.preventDefault()} className="unselectable">{code}</p>
<a>{seconds}</a>
</div>
<div className='input-captcha'>
<input type="text" placeholder="Zadejte kód" value={text} onChange={(e) => {checkCaptcha(e.target.value); setText(e.target.value)}}/>
{status === false && (<i class='bx bx-x text-color-red'></i>)}
{status === true && (<i class='bx bx-check text-color-green'></i>)}
</div>
</div>
)
}
export default Captcha;
This code is index.jsx
import React from 'react'
import Captcha from '../../../components/Captcha/Captcha';
function Index() {
function change(){
//here i need to call function from Captcha.jsx - resetToken();
}
return (
<div>
<Captcha statusOfCaptcha={resCaptchaData}/>
<button onclick={change}>Reset captcha code</button>
</div>
)
}
export default Index
It would be better to use a custom hook, to store your state, and resetToken function, So you can use it in multiple places.
For more resources about custom hooks.
https://reactjs.org/docs/hooks-custom.html
You can do this in several ways
for example you can use state manager like context api or redux.
In order to have access to your states or functions everywhere and in all pages and components
Or you can put the resetToken function in the parent component and have access to it in the child components.
export const ParentComponent = (children) => {
function resetToken {
....
}
return (
<Recapcha resetToken={resetToken} />
)
}
const Recapcha = ({resetToken}) => {
return (...)
}

react - text input give me null

(in REACT)
i have app function :
function App() {
const [welcomeMenu, setWelcomeMenu] = useState(true);
const [gameMenu, setGameMenu] = useState(false);
const [username, setUsername] = useState('');
const welcomeMenuShow = () => {
setWelcomeMenu(false);
}
const getUserName = (value) => {
setUsername(value);
console.log(username);
};
return (
<div className="App">
{
welcomeMenu ? <WelcomeMenu gameStarter={welcomeMenuShow} getUserName={getUserName}/> : null
}
</div>
);
}
in welcomemenu component i pass getUserName function to get username which user input
next in Welcome menu i have :
const WelcomeMenu = ({ gameStarter, getUserName }) => {
return (
<div className="welcome-menu">
<WelcomeText />
<WelcomeBoard gameStarter={gameStarter} getUserName={getUserName}/>
</div>
)
};
i pass get User Name in second time
in WelcomeBoard i have:
const WelcomeBoard = ({ gameStarter, getUserName }) => {
const [text, setText] = useState('');
const [warning, setWarning] = useState(false);
const checkBtn = (event) => {
if(text) {
gameStarter();
} else {
setWarning(true);
setTimeout(() => {
setWarning(false);
}, 3000);
}
};
const handleChange = (event) => {
setText(event.target.value);
};
return (
<div className="welcome-board">
<div className="username">Please enter the name</div>
<input type="text" value={text} onChange={handleChange} className="username-input" />
<button className="username-btn" onClick={() => {
getUserName(text);
checkBtn();
}}>start</button>
{warning ? <Warning /> : null}
</div>
)
};
in input onchange i make state and pass the input value on text state
next on button i have on click which active 2 function:
getUserName(text) // text is a state text with input value
checkBtn()
and after a click button in app i activate getUserName(text), this function pass the text in username state and here is a problem
when i try to see this text console.log(username) - it's give me null
but it if i try to see value console.log(value) - i see my input text
i don't understand how to fix that
react setState is async, which means those state variables are updated in the NEXT RENDER CYCLE(think of it as a thread or buffer).
try running this code if you want to understand what is happening BEHIND THE SCENES.
let renderCount = 0;
function TestApp() {
renderCount++;
const [state, setState] = useState(0);
const someRef = useRef(0);
someRef.current = state;
const someCallback = () => {
const someValue = new Date().getTime();
setState(someValue);
console.log(someRef.current, renderCount);
setTimeout(() => {
console.log(someRef.current, renderCount);
},100)
}
return <button onClick={someCallback}>clickme<button>;
}

React - I have to click a button twice to display api data

I would like to display API data on a button click. My state is set to an empty array initially and I guess that's why it shows an empty array at first click, but what could I write differently to show data on first click ? After the initial click it works.
My code:
const Search = () => {
const [textInput, setTextInput] = useState('');
const [tickers, setTickers] = useState([]);
const [prices, setPrices] = useState([]);
const [isClicked, setIsClicked] = useState(false);
const inputHandler = (e) => {
setTextInput(e.target.value);
}
const showData = async (e) => {
e.preventDefault();
const url = `https://www.alphavantage.co/query?function=TIME_SERIES_MONTHLY&symbol=${textInput}&apikey=${process.env.REACT_APP_ALPHA_VANTAGE_API_KEY}`
try {
const data = await axios.get(url);
if(data) {
setPrices([data.data['Monthly Time Series']['2021-11-30']]);
}
} catch(err) {
console.log(err)
}
console.log(prices);
setIsClicked(true);
setTextInput('');
}
return (
<StyledSearch>
<h1>Security Price Monitor App </h1>
<form onSubmit={submitSearch}>
<input type="text" value={textInput} onChange={inputHandler} placeholder='Enter Ticker'/>
<button type="submit" onClick={showData}>Search</button>
</form>
{isClicked &&
<Chart tickers = {tickers} setTickers={setTickers} prices={prices} setPrices={setPrices}/>
}
</StyledSearch>
)
}
Try to change your conditional rendering condition to:
{prices.length > 0 && (
<Chart
tickers={tickers}
setTickers={setTickers}
prices={prices}
setPrices={setPrices}
/>
)}
I think that you can remove the isClicked state. It is redundant.

React useRef with Array for dynamic

I am trying to make the component that the focus moves to the next input when each letter inputted.
I think I need multiple ref like an array but I don't know about it.
It's a sample code for the question.
function PIN({length, onChange, value}){
const inputEl = React.useRef(null);
function handleChange(e){
onChange(e);
inputEl.current.focus();
}
return (
<div>
{
new Array(length).fill(0).map((i)=>(
<input type="text" ref={inputEl} onChange={handleChange} />
))
}
</div>
)
}
You can create multiple refs
function PIN({length, onChange, value}){
const inputRefs = useMemo(() => Array(length).fill(0).map(i=> React.createRef()), []);
const handleChange = index => (e) => {
//onChange(e); // don't know about the logic of this onChange if you have multiple inputs
if (inputRefs[index + 1]) inputRefs[index + 1].current.focus();
}
return (
<div>
{
new Array(length).fill(0).map((inp, index)=>(
<input type="text" ref={inputRefs[index]} onChange={handleChange(index)} />
))
}
</div>
)
}
The ref on input is equivalent to a callback function. You can pass a method to him. The parameter received by this method is the input dom element, which you can store in an array.
import React from "react";
import "./styles.css";
export default function App() {
const inputEl = React.useRef([]);
function handleChange(i){
inputEl.current[i+1].focus();
}
return (
<div>
{
new Array(3).fill(0).map((n,i)=>(
<input
key={i}
type="text"
ref={ref=>inputEl.current.push(ref)}
onChange={()=>handleChange(i)}
/>
))
}
</div>
)
}
In your inputs, you can pass a function to the ref parameter, this will allow you to store all of your refs in an array:
let myRefs = [];
const saveThisRef = (element) => {
myRefs.push(element);
}
Then you can pass your function to each input you render:
<input type="text" ref={saveThisRef} onChange={handleChange} />
Then you can advance to the next input in the onChange handler:
// Find the index of the next element
const index = myRefs.indexOf(element) + 1;
// Focus it
if (index < myRefs.length) myRefs[index].focus();
Re-rendering the component that holds the dynamic Refs list with a different number of refs raises an exception ("Rendered more hooks than during the previous render"), as you can see in this example:
https://codesandbox.io/s/intelligent-shannon-u3yo6?file=/src/App.js
You can create a new component that renders a single and holds it's own single ref, and use the parent element to manage the current focused input, and pass this data to you'r new component, for example.
Here is an example that would actually work:
const { useState, useCallback, useEffect, useRef } = React;
const Pin = ({ length, onChange, value }) => {
const [val, setVal] = useState(value.split(''));
const [index, setIndex] = useState(0);
const arr = [...new Array(length)].map(
(_, index) => index
);
const myRefs = useRef(arr);
const saveThisRef = (index) => (element) => {
myRefs.current[index] = element;
};
function handleChange(e) {
const newVal = [...val];
newVal[index] = e.target.value;
if (index < length - 1) {
setIndex(index + 1);
}
setVal(newVal);
onChange(newVal.join(''));
}
const onFocus = (index) => () => {
const newVal = [...val];
newVal[index] = '';
setIndex(index);
setVal(newVal);
onChange(newVal.join(''));
};
useEffect(() => {
if (index < myRefs.current.length) {
myRefs.current[index].focus();
}
}, [index, length, myRefs]);
return arr.map((index) => (
<input
type="text"
ref={saveThisRef(index)}
onChange={handleChange}
onFocus={onFocus(index)}
value={val[index] || ''}
maxLength="1"
key={index}
/>
));
};
const App = () => {
const [value, setValue] = useState('');
const onChange = useCallback(
(value) => setValue(value),
[]
);
console.log('value:', value);
return (
<Pin
length={5}
value={value}
onChange={onChange}
/>
);
};
ReactDOM.render(<App />, document.getElementById('root'));
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.8.4/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.8.4/umd/react-dom.production.min.js"></script>
<div id="root"></div>
All answers will shift focus to next input when you correct an already set value. The requirement is that focus should shift when a letter is inputted, not when you remove a value.

Resources