React trying to call child (useRef) function in a functional component - reactjs

I have a sub component that is generating and displaying a random number.
I'm trying to control it from the parent.
but the ref current is null.
How can I call a child function from a parent in a functional component
https://codesandbox.io/s/busy-joliot-85yom
function App() {
const child = useRef();
const generate = () => child.current.generate();
return (
<div className="App">
<br />
<Child ref={child}></Child><br />
<button onClick={generate}>regenerate from parent (Not working)</button>
</div>
);
}
function Child(props){
const [number, setNumber] = useState(0);
const generate = () => setNumber(Math.round(Math.random() * 10));
return <div>{number} <br /> <button onClick={generate}>regenerate from child</button></div>;
}
Thanks

You cant use ref property in functional component, but you could handle any ref object by yourself, just don't call it "ref", react doesn't like it.
Check my example

Related

Custom Hook returning component rerenders all children if the individual hook state changes

Why does using a custom hook to render a component rerenders the App Component. If I toggle <Comp> or <AnotherComp> the entire App component gets rerendered same for the other component.
I tried React.memo and wrap the component again in App component but has no effect.
I read that its the way react compares and treats that functions are different betwn renders. But is there an advanced pattern people use for this purpose??
export const useCard = (params)=>{
const { ToBeWrappedComponent } = params;
const [isOpen,setIsOpen] = useState(true);
const toggle= ()=> setIsOpen(!isOpen);
const WrappedComponent = ()=>{
return (
<Collapse isOpen={isOpen} >
<button onClick= {toggle}> </button>
<ToBeWrappedComponent />
</Collapse>
)
}
return [WrappedComponent,toggle]
};
const App = ()=>{
const [Comp, toggleComp] = useCard({ToBeWrappedComponent: ()=> (<h1>Wrapped Item <h1>) });
const [AnotherComp, toggleAnotherComp] = useCard({ToBeWrappedComponent: ()=> (<h1>Another Wrapped Item <h1>) })
return (
<AnotherComp > </AnotherComp>
<Comp> </Comp>
)
}
Please note this code is just an exampple I have created to demonstrate what I am facing, I do more complex things with this apporach and just want to know about Advanced patterns to achieve it and the reason for rendering. Thanks
Because this invocation of useState is actually called within the App component's function:
const [isOpen,setIsOpen] = useState(true);
So it becomes a part of App's state, and not the wrapped component's state.
The component rerenders because every time the App is rerendered, the WrappedComponent function is created anew. It is a different function with a different address in memory, and therefore the component tree is rerendered from scratch.
This is a really weird pattern you are using. You are really overcomplicating things. Why not simply pass the render function to the wrapper component?
const TogglableComponent = ({ renderWrappedComponent, isOpen, onClick }) => {
return (
<Collapse isOpen={isOpen} >
<button onClick={onClick} />
{ renderWrappedComponent() }
</Collapse>
)
};
You then control the toggle state of each component from its parent through props. Or, if you don't care about passing the toggle state to the parent component, just store it in the wrapper:
const TogglableComponent = ({ renderWrappedComponent }) => {
const [isOpen, setIsOpen] = React.useState(false);
return (
<Collapse isOpen={isOpen} >
<button onClick={() => setIsOpen(!isOpen)} />
{ renderWrappedComponent() }
</Collapse>
)
};

How to pass a reference created by react hook of parent component to child components?

My Code:
const MyComponent: React.FC = () => {
const ParentReference = useRef(null);
return(
<Parent className="d-flex flex-row" ref={ParentReference}>
<ChildComponent
className="mr-3"
target={ParentReference.current}
/>
<AnotherChild className="mr-3" />
</Nav>
)};
As seen in the code above, I have created a reference using useRef hook and attached it to my ParentComponent.
Now am passing to ChildComponent by means of target prop and using it to do some dom manipulation inside the child.
Issue :
I am getting ParentReference as null for the first render of the component. (If I force re-render on change of the ParentReference it will update and re-render whole component then it will have value.)
How to get the ParentReference inside my child Component for initial render itself?
segFault's reference to this answer is correct. Your ref it not initialized until after your component's first render. So when you render your <ChildComponent target={ParentReference.current} />, the ref is not yet defined.
In this scenario you might consider using a useEffect to set a state variable on first render, and conditionally rendering the <ChildComponent /> once that state variable is set.
const MyComponent: React.FC = () => {
const ParentReference = useRef(null);
// define state variable defining the ref as not yet ready
const [refReady, setRefReady] = useState(false)
// On first mount, set the variable to true, as the ref is now available
useEffect( () => {
setRefReady(true)
}, [])
return(
<Parent className="d-flex flex-row" ref={ParentReference}>
{refReady && <ChildComponent
className="mr-3"
target={ParentReference.current}
/>}
<AnotherChild className="mr-3" />
</Nav>
)};

How to create a component in a custom hook that handles state within the hook?

How can I create a component in a custom hook where the hook holds the state for the component?
My attempt basically does the right thing, but drag-and-drop is not working as expected. Instead the slider will only change the value on click. I think the problem is, that the useState hook gets called outside of the X definition. But how can we create a component in a hook then where I need to work with the state of that internal component within the rest of the hook?
https://codesandbox.io/s/material-demo-milu3?file=/demo.js:0-391
import React from "react";
import Slider from "#material-ui/core/Slider";
function useComp() {
const [value, setValue] = React.useState(30);
const X = () => <Slider value={value} onChange={(_, v) => setValue(v)} />;
return { X, value };
}
export default function ContinuousSlider() {
const { X, value } = useComp();
return (
<div>
{value}
<X />
</div>
);
}
Whenever the custom hook is called (on each render), a new Slider (Broken) component is created. Since a new component is created, the event handlers are recreated as well, and the drag is cancelled. You can solve this problem in two ways:
Wrap the component in useCallback(), and pass the value when rendering the component (sandbox):
const Broken = useCallback(({ value }) => (
<Slider value={value} onChange={changeHandler} />
), [changeHandler]);
// usage
<Broken value={broken} />
Render the component in the hook, and use the include it in the component (sandbox):
function useComp() {
const [broken, setBroken] = React.useState(30);
const changeHandler = useCallback((_, v) => setBroken(v), []);
const slider = <Slider value={broken} onChange={changeHandler} />;
return { slider, broken };
}
// usage
<div>
Broken: {broken}
{slider}
OK: {ok}
<Slider value={ok} onChange={(_, v) => setOk(v)} />
</div>

Accessing HTML elements inside a React component

I have a React component InputComponent which I cannot edit, and I would like to get a reference to one of its inner divs. (for example for the purpose of focusing on the input field).
const RefsExamplePage = () => {
return (
<div>
<div>
<InputComponent
title="Test component"
></InputComponent>
</div>
</div>
)
}
export default RefsExamplePage;
How do I achieve this?
which I cannot edit
If you can't edit it, the only thing you can do is pass ref to it and hope the InputComponent have refs implemented.
e.g.
const RefsExamplePage = () => {
// use inputRef.current to access the input reference
const inputRef = React.useRef()
return (
<div>
<div>
<InputComponent
ref={inputRef}
title="Test component"
/>
</div>
</div>
)
}
If this doesn't work or give you some error, you will need to modify the InputComponent
If InputComponent doesn't provide ref you can wrap its parent (the div container) then set ref for it:
import React, { useRef } from "react";
const RefsExamplePage = () => {
const container = useRef();
return (
<div>
<div ref={container}>
<InputComponent
title="Test component"
></InputComponent>
</div>
</div>
)
}
export default RefsExamplePage;
Then you can access the child element through the div's ref.
Use useRef() to create a ref to the component itself. This way you can get the components reference and you can use .current property of it to get the underlying DOM:
const RefsExamplePage = () => {
const inputRef = useRef();
const getInput = e => {
// here get the any dom node available
inputRef.current.querySelector('input').focus();
};
return (....
<InputComponent
ref={inputRef}
onClick={getInput}
title="Test component"/> // <---if no child are passed change to self closing
....)
}

Ist there a way to pass data from child to parent using Hooks?

As an absolute newbie to React, I'd like to pass data from a child to parent component. But if I look for this question, I always find the "old" way using "state" and "callback" functions. See for example this article: https://medium.com/#ruthmpardee/passing-data-between-react-components-103ad82ebd17
Since all the guides use the traditional way, I'm very confused because they look so different from what I know. For example, instead of using "this.state" in the constructor, I use the useState() Hook.
Is there any way or maybe Hook I don't see that makes it possible to pass data from a child to a parent component?
Imagine that you have a parent component App that has the logic for handling the submit (could be any other kind of logic) of a form that is a child component of App.
The ChildForm has a local state to store its inputValue.
When you click the submit function, it will call a function onSubmit from the parent App and it will pass along its inputValue (you can pass any other value that it's present inside the component) to be processed and submitted, in this example.
So the gist of it, is:
Send a function from the parent to the child as props
The child will call that function sending some data as parameters
That function will handle the data and can trigger some action from the parent, for example
See snippet below:
function App() {
function onSubmit(formState) {
console.log('I will submit my ChildForm Input State: ' + formState);
}
return(
<ChildForm
onSubmit={onSubmit}
/>
);
}
function ChildForm(props) {
const [inputValue,setInputValue] = React.useState('');
function onChange() {
setInputValue(event.target.value);
}
return(
<React.Fragment>
<div>I am ChildForm</div>
<input type='text' value={inputValue} onChange={onChange}/>
<button onClick={()=>props.onSubmit(inputValue)}>Click to Submit through parent App</button>
</React.Fragment>
);
}
ReactDOM.render(<App/>, document.getElementById('root'));
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.8.3/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.8.3/umd/react-dom.production.min.js"></script>
<div id="root"></div>
There is no difference in flow between Functional and Class Component. You can update the value of the parent by passing the function with props and using it at the child.
parent.js
import React, {useState} from 'react';
const Parent = () => {
const [counter, setCounter] = useState(0);
return (
<Child updateCounter={setCounter}>
</Child>
)
}
child.js
const Child = (props) => {
const {updateCounter} = props;
return (
<button onClick={() => updateCounter(some value)}>
</button>
)
}
You will need to pass functions as props from the parent component to the child in order to pass data up the tree
function Child({ num, onNumClick }) {
return <span onClick={() => onNumClick(num)}>{num}</span>;
}
function Parent() {
const [numList] = useState([1, 2, 3, 4, 5]);
const [clickedItem, setClickedItem] = useState(null);
const onNumClick = num => setClickedItem(num);
return (
<div className="App">
{numList.map(n => (
<Child num={n} onNumClick={onNumClick} />
))}
{clickedItem && <p>You clicked on {clickedItem}</p>}
</div>
);
}
sandbox

Resources