How to fetch value from another component in React? - reactjs

How to fetch time when submit button is clicked from app component. I have two separate component a Timer component and a App component.
when i click on submit button from app component it should take current time snapshot from timer component and send it to app component
I don't want to use Submit in timer component
Is it possible to do in React
?
Here is app component
export default function App() {
return (
<div className="App">
<h2>Start editing to see some magic happen!</h2>
<h3>Time: </h3>
<button >Submit</button>
<Timer />
</div>
);
}
Here is Timer Component
export default function Time() {
const [counter, setCounter] = React.useState(0);
React.useEffect(() => {
let countersystem;
countersystem = setTimeout(() => setCounter(counter + 1), 1000);
return () => {
clearTimeout(countersystem);
};
}, [counter]);
return (
<div className="App">
<div>Countdown: {counter}</div>
</div>
);
}

Here you go :
You have to take 2 state on App level
1 : snapshotTrigger to trigger to timer component get current snapshot via useEffect
2 : snapshot maintain last/current snapshot
You can run the below code snippet, hope that will clear your doubts :
const { useState , useEffect } = React;
function Timer({snapshotTrigger , getSnapshot }) {
const [counter, setCounter] = useState(0);
// ------------ START : ADDED -----------
useEffect(() => {
if (snapshotTrigger) {
getSnapshot(counter);
}
}, [snapshotTrigger]);
// ------------ END : ADDED -----------
useEffect(() => {
let countersystem;
countersystem = setTimeout(() => setCounter(counter + 1), 1000);
return () => {
clearTimeout(countersystem);
};
}, [counter]);
return (
<div className="App">
<div>Countdown: {counter}</div>
</div>
);
}
function App() {
// ------------ START : ADDED -----------
const [snapshotTrigger, setSnapshotTrigger] = useState(0);
const [snapshot, setSnapshot] = useState(0);
// ------------ START : END -----------
return (
<div className="App">
<h2>Start editing to see some magic happen!</h2>
<h3>Time: {snapshot}</h3>
<button onClick={() => setSnapshotTrigger(snapshotTrigger + 1)}>Submit</button>
<Timer snapshotTrigger={snapshotTrigger} getSnapshot={setSnapshot} />
</div>
);
}
ReactDOM.render(<App />, document.getElementById('react-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="react-root"></div>

To achieve what you want I added two states to the <App> component (counter and trigger) and sent the counter set state function of the <App> component to the <Timer>, and used trigger state variable to check if the timer should continue or not, here is a snippet of what I did and also you can check this snippet for working sample:
import React from "react";
import "./styles.css";
function Timer({ onTick, active }) {
const [counter, setCounter] = React.useState(0);
React.useEffect(() => {
let countersystem;
countersystem = setTimeout(() => setCounter(counter + 1), 1000);
return () => {
clearTimeout(countersystem);
};
}, [counter]);
React.useEffect(() => {
if (active) {
onTick(counter);
}
}, [active]);
return (
<div className="App">
<div>Countdown: {counter}</div>
</div>
);
}
export default function App() {
const [counter, setCounter] = React.useState(0);
const [tigger, setTrigger] = React.useState(0);
return (
<div className="App">
<h2>Start editing to see some magic happen!</h2>
<h3>Time: {counter}</h3>
<button
onClick={() => {
setTrigger(tigger + 1);
}}
>
Submit
</button>
<Timer onTick={setCounter} active={tigger} />
</div>
);
}

Related

react useState not re rendering

I have a pretty simple useEffect hook
const [tagsWithData, setTagsWithData] = useState([]);
useEffect(() => {
....
const finalsTags = temp.map((item) => item.name);
setTagsWithData(finalsTags);
}, []);
Inside of return, I have condition to render the input tag
{tagsWithData.length !== 0 ? (
<TagsInput
selectedTags={selectedTags}
tags={tagsWithData}
/>
) : (
<TagsInput
selectedTags={selectedTags}
tags={tags}
/>
)}
The above code always stays on 0 and it does not move to the else condition.
What am I making wrong here.
Thank you
Your useEffect is not being told to update. useEffect needs to be passed the value/dependencies that it needs to (trigger the) update on. Without it, the effect will only run once on (initial) component render
const [tagsWithData, setTagsWithData] = useState([]);
useEffect(() => {
....
const finalsTags = temp.map((item) => item.name);
setTagsWithData(finalsTags);
}, [temp]); // <--- add this
Below is a small example illustrating the differences. Click on the button, and check out the output of both effectWithDep and effectWithoutDep. You'll notice only effectWithDep will update.
// Get a hook function
const { useState, useEffect } = React;
const Example = ({title}) => {
const [count, setCount] = useState(0);
const [effectWithDep, setEffectWithDep] = useState(0);
const [effectWithoutDep, setEffectWithoutDep] = useState(0);
useEffect(() => {
setEffectWithDep(count)
}, [count])
useEffect(() => {
setEffectWithoutDep(count)
}, [])
return (
<div>
<p>{title}</p>
<p>effectWithDep: {effectWithDep}</p>
<p>effectWithoutDep: {effectWithoutDep}</p>
<button onClick={() => setCount(count + 1)}>
Click me
</button>
</div>
);
};
// Render it
ReactDOM.render(
<Example title="Example using Hooks:" />,
document.getElementById("react")
);
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/17.0.1/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/17.0.1/umd/react-dom.production.min.js"></script>
<div id="react"></div>

How to add multiple events in one tag?

I'm making a counting timer which is described below with this react functional component
import {useEffect, useState, useRef} from 'react'
function Content() {
const [countdown, setCountdown] = useState(10)
const [show, setShow] = useState(true)
const ref = useRef()
function handleStart() {
ref.current = setInterval(() => {
setCountdown(prev => prev - 1)
}, 1000)
}
function handleStop() {
clearInterval(ref.current)
}
return (
<div>
<h2 style={{padding: 20}}>Time remaining: {countdown}</h2>
<button onClick={handleStart}>Start</button>
<button onClick={handleStop}>Stop</button>
</div>
)
}
export default Content;
How do I hide these two buttons after clicking one of the two.
Assuming show is the variable to control whether the buttons are visible or not.
<div>
<h2 style={{padding: 20}}>Time remaining: {countdown}</h2>
{show && <>
<button onClick={() => {
setShow(false)
handleStart()
}}>Start</button>
<button onClick={() => {
setShow(false)
handleStop()
}}>Stop</button>
</>}
</div>
React children need to return one element, so you can either wrap it in a div, or an empty element, <> </>, so you can return multiple nodes without adding a div, span, etc.
show && <></> means if show is true, the right-hand side will render, otherwise, it won't be rendered.
First, you have to introduce new state variable, you need one ror the start btn and another for the stop btn.
You have to setShow to false on either click and render the buttons conditionally depending on show variable:
const [countdown, setCountdown] = useState(10)
const [showStart, setShowStart] = useState(true)
const [showStop, setShowStop] = useState(true);
const ref = useRef()
function handleStart() {
setShowStart(false);
ref.current = setInterval(() => {
setCountdown(prev => prev - 1)
}, 1000)
}
function handleStop() {
setShowStop(false);
clearInterval(ref.current)
}
return (
<div>
<h2 style={{padding: 20}}>Time remaining: {countdown}</h2>
{showStart && <button onClick={handleStart}>Start</button>}
{showStop && <button onClick={handleStop}>Stop</button>}
</div>
)
Hope the Below Code Solver Your Problem
import React, { useEffect, useState, useRef } from 'react';
function Example() {
const [countdown, setCountdown] = useState(10);
const [show, setShow] = useState(true);
const ref = useRef();
function handleStart() {
setShow(!show);
ref.current = setInterval(() => {
setCountdown((prev) => prev - 1);
}, 1000);
}
function handleStop() {
setShow(!show);
clearInterval(ref.current);
}
return (
<div>
<h2 style={{ padding: 20 }}>Time remaining: {countdown}</h2>
{show && (
<div>
<button onClick={handleStart}>Start</button>
<button onClick={handleStop}>Stop</button>
</div>
)}
</div>
);
}
export default Example;

Local storage and persistent state in React

I want to make a like button where user can click and like something. When user clicks the button remains red even after refresh. How can i implement this?
I have this code. When i refresh the local storage gets reset. How can i get around this?
useEffect(() => {
setColor(window.localStorage.getItem('color'));
}, []);
useEffect(() => {
window.localStorage.setItem('color', color);
}, [color]);
const handleClick = () => {
setClicked(prevValue => !prevValue)
if(clicked){
setColor("red")
}else{
setColor("")
}
}
<div className="App">
<div className="container">
<button style={{backgroundColor: color}} onClick={handleClick} > +</button>
</div>
</div>
Try this approach. We need check twice localStorage first when the component mounting, second when we clicked the button. example
App.js
import { useState, useEffect } from "react";
const App = () => {
const [color, setColor] = useState("");
useEffect(() => {
const lS = window.localStorage.getItem("color");
if (lS) return setColor(lS);
localStorage.setItem("color", "");
}, []);
const handleClick = () => {
const lS = window.localStorage.getItem("color");
if (lS === "") {
localStorage.setItem("color", "red");
setColor("red");
}
if (lS !== "") {
localStorage.setItem("color", "");
setColor("");
}
};
return (
<div className="App">
<div className="container">
<button
style={{ backgroundColor: color }}
className="like-button"
onClick={handleClick}
>
+
</button>
</div>
</div>
);
};
export default App;
I have tried to duplicate this error in a sandbox. However, on my machine it works. Could it be that you have localStorage.removeItem('color') somewhere else in your project and gets called? Or maybe a problem with your browser. Here is the sandbox where it works: https://codesandbox.io/s/magical-shannon-cot7i?file=/src/App.js
I hope, it will work I have not tested it but I am sure it should work
useEffect(() => {
const storedColor = localStorage.getItem('color')
if(storedColor) {
setColor(storedColor);
}
}, []);
const handleClick = () => {
setClicked(prevValue => !prevValue)
if(clicked){
setColor("red");
localStorage.setItem('color', color);
}else{
setColor("")
}
}
return <div className="App">
<div className="container">
<button style={{backgroundColor: color}} onClick={handleClick} > + </button>
</div>
</div>

Can I render piece of a stateful component in react?

Is there any api that allow us to write code something like this:
const MyComponents = () => {
const [number, setNumber] = useState(0);
return {
Btn: <Button onPress={() => setNumber(number + 1)}>
{number}
</Button>,
Log: <p>{number}</p>
}
}
const Perent = () => <>
<div ...>
<MyComponents.Btn/>
...
...
</div>
<MyComponents.Log/>
</>
Some kind of ability to group some Component.And render them in different places...
Seems like this would be better achieved by using a Context.
E.g.
const { createContext, useState, useContext } = React;
const CountContext = createContext();
const CountContainer = ({ children }) => {
const [number, setNumber] = useState(0);
return <CountContext.Provider value={{ number, setNumber }}>
{children}
</CountContext.Provider>
};
const CountButton = () => {
const { number, setNumber } = useContext(CountContext);
return <button onClick={() => setNumber((c) => c + 1)}>
{number}
</button>;
};
const CountLog = () => {
const { number } = useContext(CountContext);
return <p>{number}</p>;
};
const SomeCountButtons = () => <div><CountButton /><CountButton /></div>;
const App = () => (<div>
<CountContainer>
<CountButton />
<CountLog />
</CountContainer>
<CountContainer>
<SomeCountButtons />
<CountLog />
</CountContainer>
</div>);
ReactDOM.render(<App />, document.getElementById('app'));
<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="app"></div>
Then any <CountButton>s or <CountLog>s that occur anywhere within the same <CountContainer> will be able to share their state.

React useContext cannot make simple counter work

I was trying to make a simple example illustrating how useContext work.
I have started with this sandbox code:
https://codesandbox.io/s/react-typescript-usecontext-lama-1v7wd
The issue is that the component Counter does not update and rerender when I click the button.
My index
import React, { useContext } from 'react'
import { MyContextProvider, MyContext } from './Context'
import { render } from 'react-dom'
const MyCounter = () => {
const context = useContext(MyContext)
const { counter } = context
const { setCounter } = context
return (
<div>
Valeur du compteur : {counter}
<br />
<button onClick={() => setCounter(counter - 1)} type="button">
-1
</button>
<button onClick={() => setCounter(counter + 1)} type="button">
+1
</button>
<br />
<button onClick={() => setCounter(0)} type="button">
RàZ
</button>
</div>
)
}
const rootElement = document.getElementById('root')
render(
<MyContextProvider>
<MyCounter />
</MyContextProvider>,
rootElement
)
My context:
type MyContextProps = {
counter: number
setCounter: Dispatch<SetStateAction<number>>
}
const MyContext = createContext({} as MyContextProps)
const MyContextProvider: React.FunctionComponent = (props) => {
const [counter, setCounter] = useState<number>(0)
return (
<MyContext.Provider
value={{
counter: 0,
setCounter: setCounter,
}}
>
{props.children}
</MyContext.Provider>
)
}
export { MyContext, MyContextProvider }
It's got to be something elementary, but I just can't see it.
Just a small error.
in your context, you have set your counter to be always zero. Change this to be counter state and your problem should be resolved.
const MyContextProvider: React.FunctionComponent = (props) => {
const [counter, setCounter] = useState<number>(0)
return (
<MyContext.Provider
value={{
counter: counter, //<-- HERE.
setCounter: setCounter,
}}
>
{props.children}
</MyContext.Provider>
)
}
As a suggestion, consider using function to get the latest state of your value when setting the next value using the setCounter function, if the next value is dependent on the previous value.
The mutation function from useState can also accept a callback function, which provides the latest current state and should return the next state value based on the previous value. This is especially helpful if setting state is in an async operation preventing stale closures.
(prevValue) => nextValue
<button onClick={() => setCounter(prevValue => prevValue - 1)} type="button">
and
<button onClick={() => setCounter(prevValue => prevValue + 1)} type="button">

Resources