I am trying to use the debounce feature as well as the ability to retain the value of textbox that I typed but it's not happening for some reason. If I comment out setMyval(e.target.value); on line #20 then the debounce works without any issue but the value I type does not show up. Whereas if I uncomment it, then the value shows in the textbox but debounce feature does not work (meaning there are multiple console logs). Please if someone can tell me why is this happening and how can I make it work, it would help me.
Below is my reactjs code:
// App.js
import React, { useState } from "react";
import ReactDOM from "react-dom";
import debounce from "./debounce";
function App() {
const [myval, setMyval] = useState("");
const handleChange = debounce(() => {
console.log("This log msg should be debounced");
}, 2000);
return (
<div className="App">
<input
type="text"
value={myval}
onChange={e => {
setMyval(e.target.value);
handleChange(e.target.value);
}}
/>
<button onClick={() => setMyval("my new value")}>Change Value</button>
</div>
);
}
const rootElement = document.getElementById("root");
ReactDOM.render(<App />, rootElement);
// debounce.js
export default function debounce(fn, wait) {
let timer;
return function() {
const context = this;
const args = arguments;
clearTimeout(timer);
timer = setTimeout(() => {
fn.apply(context, args);
}, wait);
};
}
Code at https://codesandbox.io/s/affectionate-wind-0ef3y
What I expect:
If I type a word in the textbox, the value should remain in the textbox.
Debounce should work i.e. few console logs should appear.
If I click on the "Change Value" button, it should update the value "my new value" in the textbox.
https://codesandbox.io/s/sharp-turing-w4hu9
Your debounce doesnt work, because your debounce function gets redeclared on every component update. Use useCallback to keep the reference the same and avoid reinitialization
import React, { useState, useCallback } from "react";
import ReactDOM from "react-dom";
import debounce from "./debounce";
import "./styles.css";
function App() {
const [myval, setMyval] = useState("");
const handleChange = useCallback(debounce(() => {
console.log("This log msg should be debounced");
}, 2000), []);
return (
<div className="App">
<input
type="text"
value={myval}
onChange={e => {
setMyval(e.target.value);
handleChange(e.target.value);
}}
/>
<button onClick={() => setMyval("my new value")}>Change Value</button>
</div>
);
}
const rootElement = document.getElementById("root");
ReactDOM.render(<App />, rootElement);
Related
i am new to react, i want to call the state of an outside function, for example :
export default function Child() {
const [succeeded, setSucceeded] = useState(false);
}
export default function Parent() {
if(Child.succeeded){
// do the following
}
}
i know that props are used for const objects only, and i don't want to merge both functions in a signle one to keep things organised, i would like to check for child's state to do the next step, or to callback the parent function with the new state to notify it. is there any way to do it ? Thanks a lot for your time.
Another approach is that you can use the useRef, which is very handy in some cases.
import React, {useState} from "react";
export default function Child({nameRef}) {
const [name, setName] = useState('');
React.useEffect(() => {
nameRef.current = name;
}, [name]);
return (
<>
<input nameRef={nameRef} type="text" onChange={event => setName(event.target.value)} />
</>
);
}
import React, { useState, useRef } from "react";
import ReactDOM from "react-dom";
import "./styles.css";
import Child from './Child';
function App() {
let [name, setName] = useState("Nate");
let nameRef = useRef();
const submitButton = () => {
console.log(nameRef.current);
};
return (
<div className="App">
<p>{name}</p>
<div>
<Child nameRef={nameRef} />
<button type="button" onClick={submitButton}>
Submit
</button>
</div>
</div>
);
}
const rootElement = document.getElementById("root");
ReactDOM.render(<App />, rootElement);
For some reason, the showNumber variable gets updated upon submit click, but not in in return statement. Anyhow, is there a better way to write this? Keep in mind the line in should not simply change with every input onChange but rather, only after a submit button click. I suppose one solution would be to select input via refs, but I'm hoping there's a better way. Please help.
function App() {
const [number, setNumber] = useState(1);
let showNumber = 0;
const rerender = () => {
showNumber = number;
}
const handleChange = (e) => { setNumber(e.target.value) }
return (
<header>
<input type='number' onChange={handleChange} />
<button type='submit' onClick={rerender}>submit</button>
<p>the number is {showNumber}</p>
</header>
);
}
export default App;
By Wrapping the form tag you can achieve, so when you click on the submit only it will get the form values and you can fetch the values by using name attribute
Sample Code
import React, { useState, useEffect } from "react";
import ReactDOM from "react-dom";
function App() {
const [number, setNumber] = useState(1);
const rerender = e => {
e.preventDefault();
const data = new FormData(e.target);
setNumber(data.get("number"));
};
return (
<header>
<form onSubmit={rerender}>
<input type="number" name="number" />
<button>Submit</button>
</form>
<p>the number is {number}</p>
</header>
);
}
export default App;
const rootElement = document.getElementById("root");
ReactDOM.render(<App />, rootElement);
Working codesandbox
I've seen this answer: useMemo vs. useEffect + useState , and it sums it up well for useEffect, but in my case I want to perform an expensive operation that will change the DOM as early as possible. Would useMemo() still be recommended instead of useLayoutEffect() with a state update? Does the double render of effect -> state-update negate any performance boost?
EDIT
useLayoutEffect() scenario:
useLayoutEffect(() => {
const tokens = expensiveOperationGeneratingClasses(param1)
setTokens(tokens)
},
[param1])
render (
<>
{
tokens.map(token => <span className={token.id}/>)
}
</>
)
useMemo scenario:
const tokens = useMemo(() => {
return expensiveOperationGeneratingClasses(param1)
},
[param1]
render (
<>
{
tokens.map(token => <span className={token.id}/>)
}
</>
)
Actually I realised that I'm not doing DOM operations but rather just generating the class names before the rendering of the <span> tags to avoid flickering, so I think i'm better off using useMemo, am I right?
I will try to explain where you can use LayoutEffect and Memo. Let's start with the using of LayoutEffect.
The using of LayoutEffect has some drawbacks says Dan Abramov Link 1, Link 2.It's a good explanation of where you can use these gives Kent C. Dodds.If you need an example, you can see it here Chris. Don't forget about reading for understand the difference.
Now about of the using Memo. It's also has a drawback. For what we use Memo ,and where it is used you can found here.
And now in practice.
option 1 use LayoutEffect
import React, { useState, useLayoutEffect } from "react";
import ReactDOM from "react-dom";
import "./styles.css";
const Control = () => {
const [add, setAdd] = useState(1);
return (
<div>
<div>
<PostOffice add={add} />
</div>
<div onClick={() => setAdd(add + 1)}>{"Click"}</div>
</div>
);
};
function PostOffice({ add }) {
const [letter, setLetter] = useState(add);
useLayoutEffect(() => {
console.log("useLayoutEffect");
setLetter(add);
}, [add]);
console.log(letter);
return <div className="App">{console.log(letter, "DOM")}</div>;
}
const rootElement = document.getElementById("root");
ReactDOM.render(<Control />, rootElement);
I'm not sure about this option 1, because there is an anti-pattern effect here.
option 2 use LayoutEffect
import React, { useState, useLayoutEffect } from "react";
import ReactDOM from "react-dom";
import "./styles.css";
const Control = () => {
const [add, setAdd] = useState(1);
return (
<div>
<div>
<PostOffice add={add} />
</div>
<div onClick={() => setAdd(add + 1)}>{"Click"}</div>
</div>
);
};
function PostOffice({ add }) {
const [letter, setLetter] = useState(0);
useLayoutEffect(() => {
console.log("useLayoutEffect");
setLetter(add);
}, [add]);
console.log(letter);
return <div className="App">{console.log(letter, "DOM")}</div>;
}
const rootElement = document.getElementById("root");
ReactDOM.render(<Control />, rootElement);
there will be a meaningless rendering
option useMemo
import React, { useState, useMemo } from "react";
import ReactDOM from "react-dom";
import "./styles.css";
const Control = () => {
const [add, setAdd] = useState(1);
return (
<div>
<div>
<PostOffice add={add} />
</div>
<div onClick={() => setAdd(add + 1)}>{"Click"}</div>
</div>
);
};
function PostOffice({ add }) {
const Letter = useMemo(() => {
console.log("useMemo");
return add + 1;
}, [add]);
console.log(Letter);
return <div className="App">{console.log(Letter, "DOM")}</div>;
}
const rootElement = document.getElementById("root");
ReactDOM.render(<Control />, rootElement);
And here everything works perfectly
Total
Minus useMemo 1,
Minus useLayoutEffect, 1,anti-pattern effect or meaningless rendering,adding useState,
This is why you should use useMemo.
but if there is a way not to use these hooks, it will be perfect.
I am trying to send a prop up to the top level.
At the top level I'm trying to console.log the event text
At the moment I do not want to use Redux techniques
Here is the lower level component
import React, { Component } from "react";
export default class Search extends Component {
state = {
text: ""
};
onChange = e => {
this.setState({ [e.target.name]: e.target.value });
this.props.searchUsers(this.state.text);
};
render() {
return (
<div className="ui center aligned fluid container">
<div className="ui inverted segment">
<div className="ui inverted input">
<input
type="text"
name="text"
placeholder="Type Something"
value={this.state.text}
onChange={this.onChange}
/>
</div>
</div>
</div>
);
}
}
Then I try to console.log the text through an arrow function searchUsers
import React, { useEffect, useState } from "react";
import ReactDOM from "react-dom";
import Search from "./components/Search";
const App = () => {
searchUsers = text => {
console.log(text);
};
return (
<div className="App">
<Search searchUsers={this.searchUsers} />
</div>
);
};
const rootElement = document.getElementById("root");
ReactDOM.render(<App />, rootElement);
export default App;
The Error I get
Please help me identify the problem.
In App component, you shouldn't use this and should use const
I think this should work
const App = () => {
const searchUsers = text => {
console.log(text);
};
return (
<div className="App">
<Search searchUsers={searchUsers} />
</div>
);
};
you can't use this inside stateless react component
Try this for index.js:
import React, { useEffect, useState } from "react";
import ReactDOM from "react-dom";
import Search from "./components/Search";
const App = () => {
const searchUsers = text => {
console.log(text);
};
return (
<div className="App">
<Search searchUsers={searchUsers} />
</div>
);
};
const rootElement = document.getElementById("root");
ReactDOM.render(<App />, rootElement);
export default App;
Edit:
when I console.log I only see the letter after I type the next one...So if I physically type Hello...I console.log Hell.......any way to fix that ?
This is because you are passing the state as argument after setting the state. But setState is asynchronous so by that time the state is not set. You can either pass e.target.value or using callback in setState to invoke this.props.searchUsers
For eg:
onChange = e => {
this.setState({ [e.target.name]: e.target.value });
this.props.searchUsers(e.target.value);
};
This should fix your issue.
Hope it helps!
I have been trying to come up with a custom hook to make the textfield configurable, i.e pass the set of data to a custom hook which would give me the text field that needs to be used.
The text field using the hook is being rendered as expected but I do not understand why this approach is breaking the input created using the custom hook. After every keystroke the input is losing focus and is not working as the other input that is using useState directly. It would be great if someone can explain what is going wrong and what I failed to understand.
App.js
import React, { useState } from "react";
import ReactDOM from "react-dom";
import useTextFieldBroken from "./useTextFieldBroken";
import "./styles.css";
function App() {
const [notBrokenValue, notBrokenSetValue] = useState("");
const [TextFieldBrokenInputOne] = useTextFieldBroken(
"brokenOne",
"Broken Input One",
""
);
const notBrokenOnChange = e => {
notBrokenSetValue(e.target.value);
};
return (
<div>
<label htmlFor="notBroken">
<h3>Not Broken Input</h3>
<input
id="notBroken"
onChange={notBrokenOnChange}
value={notBrokenValue}
/>
</label>
<TextFieldBrokenInputOne />
</div>
);
}
const rootElement = document.getElementById("root");
ReactDOM.render(<App />, rootElement);
customHook.js
import React, { useState } from "react";
const useTextFieldBroken = (id, label, initialValue = "") => {
const [value, setValue] = useState(initialValue);
const handleChange = e => {
setValue(e.target.value);
};
const TextField = () => {
console.log("Rendered the input field");
return (
<label htmlFor={id}>
<h3>{label}</h3>
<input
type="text"
name={id}
id={id}
onChange={handleChange}
value={value}
/>
</label>
);
};
return [TextField, value, setValue];
};
export default useTextFieldBroken;
https://codesandbox.io/s/4xj382vj40
Your input is losing focus because you're completely re-rendering the tree that creates it on each change.
The good news is that you don't need a hook to do this, just convert your hook into a functional component instead:
App.js
import React, { useState } from "react";
import ReactDOM from "react-dom";
import TextFieldBroken from "./useTextFieldBroken";
import "./styles.css";
function App() {
const [notBrokenValue, notBrokenSetValue] = useState("");
const notBrokenOnChange = e => {
notBrokenSetValue(e.target.value);
};
return (
<div>
<label htmlFor="notBroken">
<h3>Not Broken Input</h3>
<input
id="notBroken"
onChange={notBrokenOnChange}
value={notBrokenValue}
/>
</label>
<TextFieldBroken label="Previously Broken" id="previously-broken" />
</div>
);
}
const rootElement = document.getElementById("root");
ReactDOM.render(<App />, rootElement);
customHook.js
import React, { useState } from "react";
const TextFieldBroken = ({ id, label, initialValue = "" }) => {
const [value, setValue] = useState(initialValue);
const handleChange = e => {
setValue(e.target.value);
};
return (
<label htmlFor={id}>
<h3>{label}</h3>
<input
type="text"
name={id}
id={id}
onChange={handleChange}
value={value}
/>
</label>
);
};
export default TextFieldBroken;