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);
Related
I am trying to call useEffect funtion onchange of local variable, but its not working is only works if i use it with useState variable, I know there might be some basic thing here that I am not aware of.
sandbox link: https://codesandbox.io/s/affectionate-gareth-igyv7?file=/src/demo.js
import React, { useEffect, useState } from "react";
import "./styles.css";
export default function Demo() {
const [value, setValue] = useState("");
let valueOne, valueTwo;
const setValueOne = (value) => {
valueOne = value;
};
useEffect(() => {
console.log(value);
console.log(valueOne);
}, [value, valueOne]);
return (
<div>
<h1>Demo</h1>
<input
placeholder="useState"
onChange={(e) => setValue(e.target.value)}
/>
<input
placeholder="function"
onChange={(e) => setValueOne(e.target.value)}
/>
{/* {console.log(valueOne)} */}
</div>
);
}
setValueOne will not rerender your component, If you want to fire a re-render, useEffect function needs to have a useState which basically hold state between re-renders.
You can try managing your state like below, its more readable and it will work too.
import React, { useState } from "react";
import "./styles.css";
export default function Demo() {
const [valueOne, setValueOne] = useState("");
const [valueTwo, setValueTwo] = useState("");
const handleValueOne = (e) => {
setValueOne(e.target.value);
};
const handleValueTwo = (e) => {
setValueTwo(e.target.value);
};
return (
<div>
<h1>Demo</h1>
<input
value={valueOne}
placeholder="useState"
onChange={handleValueOne}
/>
<input
value={valueTwo}
placeholder="function"
onChange={handleValueTwo}
/>
{/* {console.log(valueOne)} */}
</div>
);
}
My request is very simple:
Could you please provide me with an example where an input data is passed from component A to component B using context API.
Requirements: there should be an input value entered in component A. We send the input value over to component B using context.
A and B are sibling components.
You can do this way.
Make a function in Context.js state which set the state of your input field.
//context.js state
state = {
inputFieldName: null,
setInputField: () => {
this.setState() //set value for inputFieldName here
}
}
Call that setInputField function on onChange in component A using Context and you can get inputFieldName state from Context in component B.
The required properties of the context:
1. A string property which stores the user's input
2. A method which updates the user's input.
And in your case the component A produces the input and calls the method to update it to context. The component B consumes the changes of the input from the context. it ends up with such a prototype.
import React, { useContext, useState } from "react";
import ReactDOM from "react-dom";
const MyContext = React.createContext(null);
function A() {
const { onChange } = useContext(MyContext);
const [input, setInput] = useState(null);
return (
<input
type="text"
value={input}
onChange={e => {
setInput(e.target.value);
onChange(e.target.value);
}}
/>
);
}
function B() {
const { input } = useContext(MyContext);
return <div>{input}</div>;
}
function App() {
const [input, setInput] = useState(null);
return (
<MyContext.Provider value={{ input, onChange: setInput }}>
<div>
<A />
<B />
</div>
</MyContext.Provider>
);
}
const rootElement = document.getElementById("root");
ReactDOM.render(<App />, rootElement);
Here is my try, hope it will help you
componentA.jsx
import React from "react";
const ComponentA = props => {
return (
<div>
<h1>{`ComponentA: ${props.data}`}</h1>;
<button onClick={() => props.onValueChange("value changed by ComponentA")}>
click
</button>
</div>
);
};
export default ComponentA;
componentB.jsx
import React from "react";
const ComponentB = props => {
return <h1>{`ComponentB: ${props.data}`}</h1>;
};
export default ComponentB;
App.js
import React, { Component } from "react";
import ComponentB from "./componentA";
import ComponentA from "./componentB";
class App extends Component {
state = {
data: "common value from parent"
};
handleChange = input => {
this.setState({ data: input });
};
render() {
return (
<div>
<ComponentB data={this.state.data}></ComponentB>
<ComponentA
data={this.state.data}
onValueChange={this.handleChange}
></ComponentA>
</div>
)}
}
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 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;
Although defining onClick handler properly, console.log('hi') is not printing on console
import React from 'react';
import './charComponent.css'
const CharOne = (props) =>{
return (<div>
<div className="charOne" onClick={console.log('hi')}>{props.letter}</div>
</div>
)
}
export default CharOne;
console.log('hi') is immediately invoking,
so your onClick has nothing to do (no job). See below code.
import React from 'react';
import ReactDOM from 'react-dom';
const myFunction = () => console.log('hi');
const CharOne = () => <div onClick={myFunction}>Click-Me</div>
ReactDOM.render(<CharOne />, document.getElementById('root'));
If you want to pass an argument, then use an arrow function.
import React from 'react';
import ReactDOM from 'react-dom';
const myFunction = props => console.log(props);
const CharOne = () => <div onClick={() => myFunction('Hello')}>Click-Me</div>
ReactDOM.render(<CharOne />, document.getElementById('root'));
You have to pass a function as parameter:
import React from 'react';
import './charComponent.css'
const CharOne = (props) =>{
return (<div>
<div className="charOne" onClick={() => console.log('hi')}>{props.letter}</div>
</div>
)
}
export default CharOne;