Click onClick twice in <Link> to update state - javascript

I'm trying to update my state by triggering the onClick event in my <Link>, but only after I click it twice then the output appears in my console, I have tried reading other question similar to this in stackoverflow but still could not find the solution, can anybody tell me what I did worng? Your help would be really great
import React,{useState} from 'react';
import { BrowserRouter as Router, Link } from 'react-router-dom';
const [search, setSearch] = useState("");
const [keyword, setKeyword] = useState("");
const handleChange = (e) =>{
setSearch(e.target.value);
console.log(search);
};
const handleClick = () => {
setKeyword(search);
setSearch('');
console.log(keyword);
};
return(
<div>
<input onChange={handleChange} value={search} placeholder="Search songs, artist, albums"/>
<Link onClick = {handleClick} to={`/songlist/${keyword}`}>Search</Link>
</div>
)

The thing you need to understand is : When you click, you add an event, and it's event is linked on your component and your props.
So you need to preventDefault to tell your event/props/component to act normally. Keep your actually code, but add e.preventDefault() at the begin of each event :)
Example :
const handleChange = (e) =>{
e.preventDefault();
setSearch(e.target.value);
console.log(search);
};

JS is async, so you need to handle effects using useEffect hook.
e.g. :
const [search, setSearch] = useState(null);
const [keyword, setKeyword] = useState(null);
const handleClick = () => {
setKeyword(search);
//setSearch('');
//console.log(keyword);
};
React.useEffect(()=>{
if (keyword) {
console.log(keyword);
setSearch(null);
}
},[keyword]);

can you try this, change
<Link onClick = {handleClick} to={`/songlist/${keyword}`}>Search</Link>
to
<Link to={`/songlist/${search}`}>Search</Link>

Related

React useRef is not setting even in useEffect

I am attempting to show an image in a random position according to dynamic dimensions using a ref. However, when getting the useRef's current value, I recieve undefined likely because the useRef's target div hasn't loaded yet.
To overcome this, I am using a useEffect to wait for the target div to load.
However, when I run the program the image does not move because the useRef's current value remains at 0. The
import './MouseAim.css'
import '../Game.css'
import React, {useEffect, useRef, useState} from 'react'
import TargetImg from '../../../assets/Target.png'
import _ from "lodash";
function MouseAim() {
const [start, setStart] = useState(true)
const [target, setTarget] = useState(true)
const elementDimensions = useRef()
//wait for elementDimensions to be set
useEffect(() => {
console.log('elementDimensions', elementDimensions.current?.clientHeight)
} , [elementDimensions])
return (
<div>
<div className="main-container">
{
start ?
<div ref={elementDimensions} className="aim-container">
{
target ?
<input
className="target"
type="image"
style={{position: "relative", left:elementDimensions.current?.clientHeight+"px" , top:_.random(0, elementDimensions.current?.clientHeight)+"px"}}
onClick={() => console.log("hello")}
src={TargetImg}
alt="target"
/>
:null
}
</div>
:
<div className="start-container">
<input className="start-button" type="button" value="Start Game" onClick={() => setStart(true)}/>
</div>
}
</div>
</div>
)
}
export default MouseAim
Just set your target initial state to 'false', then set it to true when 'ref' is ready
Try this one:
const [start, setStart] = useState(true)
const [target, setTarget] = useState(false)
const elementDimensions = useRef()
//wait for elementDimensions to be set
useEffect(() => {
console.log('elementDimensions', elementDimensions.current?.clientHeight)
setTarget(true)
} , [elementDimensions])
Just like you mentioned, the useRef is set very initially when the div has not been mounted.
The div mounts when start becomes true. So that's where the trigger needs to happen.
Try to pass start as the dependency variables to the useEffect.
useEffect(() => {
console.log('elementDimensions', elementDimensions.current?.clientHeight)
} , [start, elementDimensions])
PS. If it doesn't work, pass target as the dependency variable as well.
useEffect(() => {
console.log('elementDimensions', elementDimensions.current?.clientHeight)
} , [start, target, elementDimensions])
I think separating your code into subcomponents will make this much simpler, as MouseAim has too much responsibility. In general, giving your components one clearly-defined purpose is a good way to simplify your codebase.
const AimContainer = () => {
const containerRef = useRef()
const [containerSize, setContainerSize] = useState();
useEffect(() => {
// set container size state here
}, [])
return <div ref={containerRef}>...</div>
}
const MouseAim = () => {
const [hasStarted, setHasStarted] = useState(false);
return <div>
{ hasStarted ?
<AimContainer />
:
<button onClick={() => setHasStarted(true)}>Start</button>
}
</div>
}
AimContainer will render before running useEffect, so you may also want to avoid rendering the target until containerSize is set.

Why KeyboardEvent isn't working with this Input element in react?

I'm working with controlled input elements at work and I'm stuck.
Basically, I need to autofill some input elements in a form, but the problem is that I need to fill it in a way that simulates the user input (in this case, typing) in order to trigger the onChange function's logic. So, because of that. I need to emulate the typing behavior and not just set the value for the element.
Despite having searched for previous questions and reading docs about KeyboardEvent, I haven't been able to make this work.
Currently, I'm experimenting in a Codesandbox just for making things easier, but even with this simple environment, I can't manage to get this to work.
Here's the code and its Codesandbox link
import { useRef, useState, useEffect } from "react";
import "./styles.css";
export default function App() {
const [state, setState] = useState();
const inputRef = useRef();
const event = new KeyboardEvent("keypress", { key: 99 });
useEffect(() => {
inputRef.current.dispatchEvent(event);
}, [inputRef]);
const onChange = (e) => {
setState(e.target.value);
};
return (
<div className="App">
<h1>{state}</h1>
<input
type="text"
id="name"
onChange={onChange}
ref={inputRef}
value={state}
/>
</div>
);
}
Hopefully one of you guys could give me a hand with this.
Thanks for reading!
Related to the comments:
I think that it shouldn't be necessary to be dispatching a keypress event to get your special effect logic to run.
For example, you can use a useEffect which just runs on initial render to trigger whatever special logic you want -- and this way you can just have a regular initial value for the form state.
import { useState, useEffect } from "react";
import "./styles.css";
export default function App() {
// In the useState call, you can initialize the value.
const [state, setState] = useState("initial value");
const specialEffectFunction = () => {
// here's the code for the special effect you want to run on load
console.log('this is the special onChange effect')
}
useEffect(() => {
// This will trigger the special function which you want to run
// when the app loads
specialEffectFunction();
// if it really HAS to be the `onChange` function that's called,
// then you'll need to call that with a fake ChangeEvent.. but I don't
// think that should be necessary...
}, [])
const onChange = (e) => {
setState(e.target.value);
};
return (
<div className="App">
<h1>{state}</h1>
<input
type="text"
id="name"
onChange={onChange}
value={state}
/>
</div>
);
}
I couldn't fix the problem with Keyboard Event for my lack of knowledge about it, but I hope I managed to solve the problem of emulating a human autofill the input using the below code.
function AutoFillInput({ finalValue }: { finalValue: string }) {
const [inputValue, setInputValue] = useState('');
const [sliceStart, setSliceStart] = useState(0);
const changeHandler = useCallback((event) => {
setInputValue(event.target.value);
}, []);
useEffect(function handleFinalValueChange() {
setInputValue('');
if (sliceStart < finalValue.length)
setSliceStart(x => x + 1);
}, [finalValue]);
useEffect(function handleSlice() {
setInputValue(finalValue.slice(0, sliceStart));
if (sliceStart < finalValue.length) {
setTimeout(() => {
setSliceStart(x => x + 1);
}, 800);
}
}, [sliceStart]);
return (
<input
value={inputValue}
onChange={changeHandler}
placeholder={'Auto fill input'}
/>
)
}
function App() {
return (
<div >
<AutoFillInput finalValue={'hello world'} />
</div>
);
}
export default App;

Why my fetch is not working, while I use Usestate

I want to make a component, which should have an input and button. The button should do some kind of work, I want it to show the information that I need to receive with fetch. In this case my code is not working, it shows a lot of [objects], however I want it to show the information from the base. if you can please help... : )
import React, { useState } from 'react';
const App = () => {
const [User, setUser] = useState({num: ""})
const [Open, setOpen] = useState(false)
const users = () => {
fetch("https://jsonplaceholder.typicode.com/users")
.then((response) => response.json())
.then((json) => {
document.write(json);
});
};
return( <>
<input name="Num" />
<button onClick={users}> Click Me for users </button>
</>)
}
export default App ```
You should almost never use document.write
Following is one of they ways you can solve your problem
import React, { useState } from "react";
const App = () => {
const [User, setUser] = useState({ num: "" });
const [Open, setOpen] = useState(false);
const [users, setUsers] = useState([]);
const fetchAndSetUsers = () => {
fetch("https://jsonplaceholder.typicode.com/users")
.then((response) => response.json())
.then((users) => {
setUsers(users);
});
};
return (
<>
<input name="Num" />
<button onClick={fetchAndSetUsers}> Click Me for users </button>
{users.map((user) => {
return <div>{user.name}</div>;
})}
</>
);
};
export default App;
Change the line of
document.write
to
setUsers(json)
You shouldn't manipulate UI directly in React using DOM methods since React uses Virtual DOM concept to update UI
document.write receive html element as parameter not a json object, if you just want to check if the fetch work you can do JSON.stringigy(json)
PS: sorry for my poor english

Using React Javascript (Form-Onsubmit & calling API not working properly)

i am a bit puzzled with the logic when reading the below code, although the code is working but not exactly as i would like it to behave.
3 queries i have if some one can please clarify.
1- As i understand useEffect is used to invoke the function after render, but in the below code, once the form is sumbitted (onSubmit={credentialVerify}) it will call the credentialVerify() function as below, so i dont think we need useEffect here, but still the code doesnt call the API unless i use the useEffect statement.
2- Also doesnt wait for me to enter my credentails first and as soon as i go to the Signin page it will fetch the API’s (when using useEffect ) and shows the result in the windows, but i try to design in a way that when i click button then it will fetch the API
3- when in the form onsubmit call the credentialVerify function, i have console.log(e) but it is showing as undefined, but as i understand onsubmit will call the function and through the event argument by default.
Below is the snippet of my code.
Any help Appreciated.
import React, { useState, useEffect } from "react";
import "../App.css";
import { Link } from "react-router-dom";
function Signin() {
const [name, setName] = useState("");
const [password, setPassword] = useState("");
const updateName = (e) => {
setName(e.target.value);
};
const updatePassword = (e) => {
setPassword(e.target.value);
};
const [items, setItems] = useState([]);
useEffect(() => { //Point-1 useEffect- API not call atall without this statement
credentialVerify();
}, []);
const credentialVerify = async (e) => {
console.log(e); //Point-3 this is coming as undefined
const data1 = await fetch("http://localhost:5000/api/customers");
const incomingdata = await data1.json();
console.log(data1);
console.log(incomingdata);
console.log(name, password);
setItems(incomingdata);
};
return (
<div>
<div>
{
<form className="formstyle" onSubmit={credentialVerify}>
<input
type="text"
placeholder="Username"
name="username"
value={name}
onChange={updateName}
/>
<input
type="text"
placeholder="Password"
name="password"
value={password}
onChange={updatePassword}
/>
<button type="submit">Submit</button>
</form>
}
</div>
<div>
{items.map((entry) => {
let key = entry.email;
let valuefirst = entry.firstName;
let valuelast = entry.created_at;
return (
<p key={key}>
{key}: {valuefirst}bb {valuelast}
</p>
);
})}
</div>
</div>
);
}
export default Signin;
For your first question, you are correct - it doesn't make sense to call credentialVerify when your component renders for the first time since that seems to be the handler for when your form gets submitted. Unless you're fetching data prior to displaying your form, you can drop the useEffect hook entirely.
This is also takes care of your second question because the hook will run once when your component renders for the first time, which is indicated by the empty array [] used as a dependency array of the useEffect hook. This is equivalent to componentDidMount in a class-based component, but again, it doesn't make sense to call credentialVerify at this point.
As for your third question, you should probably do something like the following:
const credentialVerify = event => {
event.preventDefault();
(async () => {
const data = await fetch("http://localhost:5000/api/customers")
.then(res => res.json());
.catch(e => e);
console.log(incomingData);
// ...
})();
}
Since you're passing an asynchronous function as your event handler, you might have issues accessing the SyntheticEvent object due to the reasons stated in React docs:
The SyntheticEvent is pooled. This means that the SyntheticEvent object will be reused and all properties will be nullified after the event callback has been invoked. This is for performance reasons. As such, you cannot access the event in an asynchronous way.
reactjs.org/docs/events.html#event-pooling
Your final component should look like the following:
function Signin() {
const [name, setName] = useState("");
const [password, setPassword] = useState("");
const [items, setItems] = useState([]);
const updateName = e => {
setName(e.target.value);
};
const updatePassword = e => {
setPassword(e.target.value);
};
const credentialVerify = event => {
event.preventDefault();
(async () => {
const incomingdata = await fetch("http://localhost:5000/api/customers")
.then(res => res.json())
.catch(e => e);
console.log(incomingdata);
console.log(name, password);
setItems(incomingdata);
})();
};
return (
<div>...</div>
);
}

React handleClick button throwing error while using hooks

import React, {useState} from "react"
const App = () => {
const {loggedIn, setLoggedIn} = useState(false)
const handleClick = () => {
setLoggedIn(!loggedIn)
}
return (
<div>
<h1>You are {loggedIn ? 'logged in' : 'logged out'}</h1>
<button onClick={handleClick}>{loggedIn ? 'Log Out' : 'Log In'}</button>
</div>
)
}
export default App
I was writing some code using hooks, and when I click on the button, nothing happens and console shows unknown error message.
I tried changing it to:
() => handleClick
handleClick()
but they all don't work.
What is wrong with the code?
The problem is useState is returning with [] instead of {}.
You should have the following instead:
const [loggedIn, setLoggedIn] = useState(false);
+1 suggestion:
Also it is better to use the callback option when using setLoggedIn in order to capture the previous version of the state as the following:
const handleClick = () => {
setLoggedIn(prev => !prev);
}
I hope this helps!
change const {loggedIn, setLoggedIn} = useState(false)
To : const [loggedIn, setLoggedIn] = useState(false)
Dont use {} to declare useState variable and its setter function use [] these instead.
You are destructuring the state value and change handler incorrectly. It returns a tuple so you need to get the values like this:
const [loggedIn, setLoggedIn] = useState(false)

Categories

Resources