Odd bug, state issue, state in child component mirroring parent state - javascript

Could anybody please tell me what I missed here. I have an odd issue with state of child component. As soon as I handle submit and callback function sent from parent component by props invokes in child component for setting state in parent, the state in a child component itself changes oddly, namly it mirroring a state from parent component, and getting it's properties.
I have no idea how it could be, as in fact besides props which sent a callback to a child component there're no any other link between them.
Can it be that setState in a callback that set state on parent, also mutate the child state ?
/* Parent component */
import React from "react";
import AddData from "./AddData.jsx";
function App() {
const [users, setUsers] = React.useState([]);
const handleForm = (user) => {
user.id = Math.random();
const newState = [...users, user];
setUsers(newState);
};
const usersList = users.map((user) => (
<div key={user.id}>
<p>
Name: {user.name} ID: {user.id}
</p>
</div>
));
return (
<div>
{usersList}
<AddData handleForm={handleForm} />
</div>
);
}
export default App;
/* Child component */
import React from "react";
class AddData extends React.Component {
state = {
name: null,
};
handleName = (e) => {
const { target } = e;
this.setState({
name: target.value,
});
};
handleSubmit = (e) => {
e.preventDefault();
this.props.handleForm(this.state);
};
render() {
console.log("AddData STATE->", this.state); //{name: 'Tom', id: 0.791334565427714}
return (
<div>
<form onSubmit={this.handleSubmit}>
<label htmlFor="name">Name:</label>
<input type="text" id="name" onChange={this.handleName} />
<button>Submit</button>
</form>
</div>
);
}
}
export default AddData;
In that console.log in child component that log a child component state, after submiting a form, I get two properties, while it seems it should be only one there. An id property is in fact property from the parent state.

The state object reference you pass to the handleForm function is the same object as user in the function body. Updating properties applies across all references.
If you want to break the reference and only keep the new data in the parent component, I would suggest using this
const handleForm = (user) => {
setUsers((prev) => prev.concat({ ...user, id: Math.random() }));
};
See Spread in object literals...
Shallow-cloning (excluding prototype) or merging of objects
See also Functional updates for why you should use the callback version of setUsers()
You could also break the reference at the caller side
this.props.handleForm({ ...this.state });

Related

How to access particular child state from parent with multiple children?

Let's say I have a parent component in React with 3 separate child components of the same component class (meaning, within class Parent I have 3 Child components). How do I access each child's state within the Parent component?
My initial thoughts are to have a separate variable for each Child's state that I want to access (I only want to access the filled variable for each child). But I feel that this is certainly a sloppy solution to something that is already in place with React, so would appreciate any pointers.
Example code below for illustration purposes.
const Parent = (props) => {
return (
<div>
<Child/>
<Child/>
<Child/>
</div>
);
}
const Child = (props) => {
const [filled, setFilled] = useState(false);
return (
<div></div>
);
}
Perhaps the better question is how do I access the particular child? And once accessed, how do I access its filled state (callback function)? I've read about useRef, is that where I should be looking here?
If what you are trying to do is report back to the parent the childs's state, you can do that by passing down from the parent, via props, a function to report that state back, as such:
const Parent = (props) => {
const reportChildState = (value) =>{
//do something with the child filled state
}
return (
<div>
<Child reportState={reportChildState}/>
<Child reportState={reportChildState}/>
<Child reportState={reportChildState}/>
</div>
);
}
const Child = (props) => {
//in here you can call props.reportChildState(filled)
const [filled, setFilled] = useState(false);
return (
<div></div>
);
}
As far as I'm aware there isnt a way to access a child components state from the parent. The only solutions are to either pass the state object into the child as a prop, using the context API or using a thirst party state management such as redux.
I wouldnt use useref for accessing a child's state.
You actually wouldn't directly access the children to retrieve their states. What you want here is a Context that encompases the parent component. Check out the React Context API. In short, you can create a "context" that contains states that need to be shared between multiple components. Once the context is made, a Provider is also created with that context. This provider is a component that accepts a value prop. This prop contains an object of all the state values and setter functions within the context. Child components of a Provider component can use the useContext hook to retrieve the values and functions from the value prop of the Provider.
Code example:
MyContext.js
import React, {createContext, useState} from 'react';
// Create the context and give it default values
export const MyContext = createContext(defaultValuesObject);
// We create a component that wraps around the provider, and is stateful. The states and their setters are placed into the provider, which is then returned.
const MyProvider = (props) => {
const [firstName, setFirstName] = useState("")
const [lastName, setLastName] = useState("")
const dataToShare = {
firstName,
setFirstName,
lastName,
setLastName,
}
// Return the context provider with the data already inside, and fill the children.
return (
<MyContext.Provider value={dataToShare}>
{props.children}
</MyContext.Provider>
)
}
We have the provider now. In your parent component's code, use the useContext hook to give the component access to the values and functions stored inside the provider component.
MyParentComponent.jsx
import React, {useContext} from 'react';
import MyProvider, { MyContext } from 'MyContext'
const MyComponent = (props) => {
// useContext returns the value stored in the provider so we can use it and the functions inside. The context is maintained inside the states in the provider.
const providerValue = useContext(MyContext)
// OR
const { firstName, setFirstName, lastName, setLastName } = useContext(MyContext)
return (
<div>
// Provide the first child component with the values and functions from the context
<MyChildComponentOne someProp={providerValue} />
// Provide the second child component with the values and functions from the context
<MyChildComponentTwo someProp={providerValue} />
// Provide the second third component with the values and functions from the context
<MyChildComponentThree someProp={providerValue} />
<div/>
)
}
Now, we still won't be able to use the context without a provider, which MUST wrap around the component(s) calling useContext for that specific context. Assuming the ParentComponent is used inside of App.jsx:
App.jsx
...imports and whatever other code you have in here
/// The jsx for the App component or whatever component calls MyParentComponent
return <div>
<MyProvider>
<MyParentComponent>
</MyProvider>
</div>
To re-iterate, you are taking the state OUT of the child/parent components, and putting them INTO the Provider created by the Context. Child components of the Provider can call useContext and gain access to the data and functions in the Provider's value prop.
There is no direct way to pass information from the child to the parent, only the other way around.
But that means you can also pass functions from the parent to the child! One common pattern for achieving what you need would be to pass the state set function to the child, so it can alter the parent's state. Like so:
const Parent = (props) => {
const [childStates, setChildStates] = useState({ child1: '', child2: '', child3: '' })
return (
<div>
<Child
state={childStates.child1}
setState={(val) => setChildStates((prev) => ({ ...prev, child1: val }))}
/>
<Child
state={childStates.child2}
setState={(val) => setChildStates((prev) => ({ ...prev, child2: val }))}
/>
<Child
state={childStates.child3}
setState={(val) => setChildStates((prev) => ({...prev, child3: val }))}
/>
</div>
);
}

React component doesn't rerender with the same props but child state changes

i have the following problem:
I have parent component (where is button, and array of child components to render).
To each child i pass props and child uses it as state, then changes it.
The problem is that children doesn't rerender.
As it may seem not understandable, here is something more clear (i hope):
Here is the simplified version of child.js
export default function ChildComponent(props) {
const [open, setOpen] = React.useState(props.open);
const handleClick = () => {
setOpen(true);
}; /* i actually never use handleClick */
const handleClose = (event) => {
setOpen(open => !open);
};
return (
<div>
<SomeComponent hideAfterTimeMs={1000} onClose={handleClose}/>
</div>
);
}
Parent:
import React from "react";
import Child from "./demo";
class MyClass extends React.Component {
constructor(props) {
super(props);
this.state = {
something: false,
};
}
displayKids = () => {
const a = [];
for (let i = 0; i < 1; i++) {
a.push(<Child open={true} key={i} message={"Abcd " + i} />);
}
return a;
};
handleChange = e => {
this.setState(prevState => ({
something: !prevState.something,
}));
};
render() {
return (
<div>
<button onClick={this.handleChange}>Nacisnij mnie</button>
{this.displayKids()}
</div>
);
}
}
export default MyClass;
So basically the child component renders,
and sets its "open" to false,
and when i click button again
i hoped for displaying child again,
but nothing happens.
Child renders something that disappears after a few seconds.
Keys help React identify which items have changed, are added, or are
removed. Keys should be given to the elements inside the array to give
the elements a stable identity.
You are using the index as a key. Please try to use a unique key. E.g. child id or random hash code.
If the key is unique and new it will re-render. Right now it is not re-rendering because the key is the same.
Check out: https://reactjs.org/docs/lists-and-keys.html#keys
It doesn't look like your components are linked in any meaningful way. Clicking the button on the My Class component updates the something state, but that state is not passed to the Child component.
Similarly, the SomeComponent component handles its own close, and tells the Child component it is closed via handleClose - but that is not communicated to the parent and neither does the parent or Child communicate any open state to SomeComponent.
Changing some state on Child will not rerender it's own children. Something new has to be passed as a prop for that to happen.

Convert <Class Component> to <Stateless Functional Component> using Hooks with a callback function

I am trying to convert Class Component to Stateless Functional Component using React Hooks concept
I am working with react-jsonschema-form - Custom field components reference link
const schema = {
type: "object",
required: ["lat", "lon"],
properties: {
lat: {type: "number"},
lon: {type: "number"}
}
};
// Define a custom component for handling the root position object
class GeoPosition extends React.Component {
constructor(props) {
super(props);
this.state = {...props.formData};
}
onChange(name) {
return (event) => {
this.setState({
[name]: parseFloat(event.target.value)
}, () => this.props.onChange(this.state));
};
}
render() {
const {lat, lon} = this.state;
return (
<div>
<input type="number" value={lat} onChange={this.onChange("lat")} />
<input type="number" value={lon} onChange={this.onChange("lon")} />
</div>
);
}
}
// Define the custom field component to use for the root object
const uiSchema = {"ui:field": "geo"};
// Define the custom field components to register; here our "geo"
// custom field component
const fields = {geo: GeoPosition};
// Render the form with all the properties we just defined passed
// as props
render((
<Form
schema={schema}
uiSchema={uiSchema}
fields={fields} />
), document.getElementById("app"));
I am converting the above code like this.
function GeoPosition(props) {
const [state, setState] = React.useState({ ...props.formData });
const onChange = name => {
return event => {
setState(
{
[name]: parseFloat(event.target.value)
},
() => props.onChange(state) // GETTING ERROR - UNABLE TO USE CALLBACK
);
};
};
const { lat, lon } = state;
return (
<div>
<input type="number" value={lat} onChange={onChange("lat")} />
<input type="number" value={lon} onChange={onChange("lon")} />
</div>
);
}
It throws an error, I think, I need to use React.useEffect(), but don't how to implement it. kindly any react experts support.
index.js:1375 Warning: State updates from the useState() and
useReducer() Hooks don't support the second callback argument. To
execute a side effect after rendering, declare it in the component
body with useEffect().
The setter function from useState does not accept a second argument: [hooks] useState - "setState" callback. I'm not sure you need to use useEffect here, you can just call props.onChange(state) after setting the state values. Also note that you need to concat the existing state with the new state values because setState will overwrite existing state.
const onChange = name => {
return event => {
setState(state => {
...state,
[name]: parseFloat(event.target.value)
})
props.onChange(state);
};
};
If you really need to make sure props.onChange is called only after the new value has been set on the current component's state, you can track the state in useEffect, altho you'll need to use a custom function for deep comparison: react useEffect comparing objects
useEffect(() => {
props.onChange(state);
}, [deepCompare(state)])

ReactJS - How to expose your Component to external events? [duplicate]

I have two components:
Parent component
Child component
I was trying to call Child's method from Parent, I tried this way but couldn't get a result:
class Parent extends Component {
render() {
return (
<Child>
<button onClick={Child.getAlert()}>Click</button>
</Child>
);
}
}
class Child extends Component {
getAlert() {
alert('clicked');
}
render() {
return (
<h1 ref="hello">Hello</h1>
);
}
}
Is there a way to call Child's method from Parent?
Note: Child and Parent components are in two different files.
First off, let me express that this is generally not the way to go about things in React land. Usually what you want to do is pass down functionality to children in props, and pass up notifications from children in events (or better yet: dispatch).
But if you must expose an imperative method on a child component, you can use refs. Remember this is an escape hatch and usually indicates a better design is available.
Previously, refs were only supported for Class-based components.
With the advent of React Hooks, that's no longer the case
Modern React with Hooks (v16.8+)
const { forwardRef, useRef, useImperativeHandle } = React;
// We need to wrap component in `forwardRef` in order to gain
// access to the ref object that is assigned using the `ref` prop.
// This ref is passed as the second parameter to the function component.
const Child = forwardRef((props, ref) => {
// The component instance will be extended
// with whatever you return from the callback passed
// as the second argument
useImperativeHandle(ref, () => ({
getAlert() {
alert("getAlert from Child");
}
}));
return <h1>Hi</h1>;
});
const Parent = () => {
// In order to gain access to the child component instance,
// you need to assign it to a `ref`, so we call `useRef()` to get one
const childRef = useRef();
return (
<div>
<Child ref={childRef} />
<button onClick={() => childRef.current.getAlert()}>Click</button>
</div>
);
};
ReactDOM.render(
<Parent />,
document.getElementById('root')
);
<script src="https://unpkg.com/react#16/umd/react.development.js" crossorigin></script>
<script src="https://unpkg.com/react-dom#16/umd/react-dom.development.js" crossorigin></script>
<div id="root"></div>
Documentation for useImperativeHandle() is here:
useImperativeHandle customizes the instance value that is exposed to parent components when using ref.
Legacy API using Class Components (>= react#16.4)
const { Component } = React;
class Parent extends Component {
constructor(props) {
super(props);
this.child = React.createRef();
}
onClick = () => {
this.child.current.getAlert();
};
render() {
return (
<div>
<Child ref={this.child} />
<button onClick={this.onClick}>Click</button>
</div>
);
}
}
class Child extends Component {
getAlert() {
alert('getAlert from Child');
}
render() {
return <h1>Hello</h1>;
}
}
ReactDOM.render(<Parent />, document.getElementById('root'));
<script src="https://unpkg.com/react#16/umd/react.development.js" crossorigin></script>
<script src="https://unpkg.com/react-dom#16/umd/react-dom.development.js" crossorigin></script>
<div id="root"></div>
Callback Ref API
Callback-style refs are another approach to achieving this, although not quite as common in modern React:
const { Component } = React;
const { render } = ReactDOM;
class Parent extends Component {
render() {
return (
<div>
<Child ref={instance => { this.child = instance; }} />
<button onClick={() => { this.child.getAlert(); }}>Click</button>
</div>
);
}
}
class Child extends Component {
getAlert() {
alert('clicked');
}
render() {
return (
<h1>Hello</h1>
);
}
}
render(
<Parent />,
document.getElementById('app')
);
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react-dom.min.js"></script>
<div id="app"></div>
You can use another pattern here:
class Parent extends Component {
render() {
return (
<div>
<Child setClick={click => this.clickChild = click}/>
<button onClick={() => this.clickChild()}>Click</button>
</div>
);
}
}
class Child extends Component {
constructor(props) {
super(props);
this.getAlert = this.getAlert.bind(this);
}
componentDidMount() {
this.props.setClick(this.getAlert);
}
getAlert() {
alert('clicked');
}
render() {
return (
<h1 ref="hello">Hello</h1>
);
}
}
What it does is to set the parent's clickChild method when child is mounted. In this way when you click the button in parent it will call clickChild which calls child's getAlert.
This also works if your child is wrapped with connect() so you don't need the getWrappedInstance() hack.
Note you can't use onClick={this.clickChild} in parent because when parent is rendered child is not mounted so this.clickChild is not assigned yet. Using onClick={() => this.clickChild()} is fine because when you click the button this.clickChild should already be assigned.
Alternative method with useEffect:
Parent:
const [refresh, doRefresh] = useState(0);
<Button onClick={() => doRefresh(prev => prev + 1)} />
<Children refresh={refresh} />
Children:
useEffect(() => {
performRefresh(); //children function of interest
}, [props.refresh]);
Here I will give you the four possible combinations that can happen:
Class Parent | Hook Child
Hook Parent | Class Child
Hook Parent | Hook Child
Class Parent | Class Child
1. Class Parent | Hook Child
class Parent extends React.Component {
constructor(props) {
super(props)
this.myRef = React.createRef()
}
render() {
return (<View>
<Child ref={this.myRef}/>
<Button title={'call me'}
onPress={() => this.myRef.current.childMethod()}/>
</View>)
}
}
const Child = React.forwardRef((props, ref) => {
useImperativeHandle(ref, () => ({
childMethod() {
childMethod()
}
}))
function childMethod() {
console.log('call me')
}
return (<View><Text> I am a child</Text></View>)
})
2. Hook Parent | Class Child
function Parent(props) {
const myRef = useRef()
return (<View>
<Child ref={myRef}/>
<Button title={'call me'}
onPress={() => myRef.current.childMethod()}/>
</View>)
}
class Child extends React.Component {
childMethod() {
console.log('call me')
}
render() {
return (<View><Text> I am a child</Text></View>)
}
}
3. Hook Parent | Hook Child
function Parent(props) {
const myRef = useRef()
return (<View>
<Child ref={myRef}/>
<Button title={'call me'}
onPress={() => myRef.current.childMethod()}/>
</View>)
}
const Child = React.forwardRef((props, ref) => {
useImperativeHandle(ref, () => ({
childMethod() {
childMethod()
}
}))
function childMethod() {
console.log('call me')
}
return (<View><Text> I am a child</Text></View>)
})
4. Class Parent | Class Child
class Parent extends React.Component {
constructor(props) {
super(props)
this.myRef = React.createRef()
}
render() {
return (<View>
<Child ref={this.myRef}/>
<Button title={'call me'}
onPress={() => this.myRef.current.childMethod()}/>
</View>)
}
}
class Child extends React.Component {
childMethod() {
console.log('call me')
}
render() {
return (<View><Text> I am a child</Text></View>)
}
}
https://facebook.github.io/react/tips/expose-component-functions.html
for more answers ref here Call methods on React children components
By looking into the refs of the "reason" component, you're breaking encapsulation and making it impossible to refactor that component without carefully examining all the places it's used. Because of this, we strongly recommend treating refs as private to a component, much like state.
In general, data should be passed down the tree via props. There are a few exceptions to this (such as calling .focus() or triggering a one-time animation that doesn't really "change" the state) but any time you're exposing a method called "set", props are usually a better choice. Try to make it so that the inner input component worries about its size and appearance so that none of its ancestors do.
I wasn't satisfied with any of the solutions presented here. There is actually a very simple solution that can be done using pure Javascript without relying upon some React functionality other than the basic props object - and it gives you the benefit of communicating in either direction (parent -> child, child -> parent). You need to pass an object from the parent component to the child component. This object is what I refer to as a "bi-directional reference" or biRef for short. Basically, the object contains a reference to methods in the parent that the parent wants to expose. And the child component attaches methods to the object that the parent can call. Something like this:
// Parent component.
function MyParentComponent(props) {
function someParentFunction() {
// The child component can call this function.
}
function onButtonClick() {
// Call the function inside the child component.
biRef.someChildFunction();
}
// Add all the functions here that the child can call.
var biRef = {
someParentFunction: someParentFunction
}
return <div>
<MyChildComponent biRef={biRef} />
<Button onClick={onButtonClick} />
</div>;
}
// Child component
function MyChildComponent(props) {
function someChildFunction() {
// The parent component can call this function.
}
function onButtonClick() {
// Call the parent function.
props.biRef.someParentFunction();
}
// Add all the child functions to props.biRef that you want the parent
// to be able to call.
props.biRef.someChildFunction = someChildFunction;
return <div>
<Button onClick={onButtonClick} />
</div>;
}
The other advantage to this solution is that you can add a lot more functions in the parent and child while passing them from the parent to the child using only a single property.
An improvement over the code above is to not add the parent and child functions directly to the biRef object but rather to sub members. Parent functions should be added to a member called "parent" while the child functions should be added to a member called "child".
// Parent component.
function MyParentComponent(props) {
function someParentFunction() {
// The child component can call this function.
}
function onButtonClick() {
// Call the function inside the child component.
biRef.child.someChildFunction();
}
// Add all the functions here that the child can call.
var biRef = {
parent: {
someParentFunction: someParentFunction
}
}
return <div>
<MyChildComponent biRef={biRef} />
<Button onClick={onButtonClick} />
</div>;
}
// Child component
function MyChildComponent(props) {
function someChildFunction() {
// The parent component can call this function.
}
function onButtonClick() {
// Call the parent function.
props.biRef.parent.someParentFunction();
}
// Add all the child functions to props.biRef that you want the parent
// to be able to call.
props.biRef {
child: {
someChildFunction: someChildFunction
}
}
return <div>
<Button onClick={onButtonClick} />
</div>;
}
By placing parent and child functions into separate members of the biRef object, you 'll have a clean separation between the two and easily see which ones belong to parent or child. It also helps to prevent a child component from accidentally overwriting a parent function if the same function appears in both.
One last thing is that if you note, the parent component creates the biRef object with var whereas the child component accesses it through the props object. It might be tempting to not define the biRef object in the parent and access it from its parent through its own props parameter (which might be the case in a hierarchy of UI elements). This is risky because the child may think a function it is calling on the parent belongs to the parent when it might actually belong to a grandparent. There's nothing wrong with this as long as you are aware of it. Unless you have a reason for supporting some hierarchy beyond a parent/child relationship, it's best to create the biRef in your parent component.
I hope I'm not repeating anything from above but what about passing a callback prop that sets the function in the parent? This works and is pretty easy. (Added code is between the ////'s)
class Parent extends Component {
/////
getAlert = () => {} // initial value for getAlert
setGetAlertMethod = (newMethod) => {
this.getAlert = newMethod;
}
/////
render() {
return (
<Child setGetAlertMethod={this.setGetAlertMethod}>
<button onClick={this.getAlert}>Click</button>
</Child>
);
}
}
class Child extends Component {
/////
componentDidMount() {
this.props.setGetAlertMethod(this.getAlert);
}
/////
getAlert() => {
alert('clicked');
}
render() {
return (
<h1 ref="hello">Hello</h1>
);
}
}
you can use ref to call the function of the child component from the parent
Functional Component Solution
in functional component, you have to use useImperativeHandle for getting ref into a child like below
import React, { forwardRef, useRef, useImperativeHandle } from 'react';
export default function ParentFunction() {
const childRef = useRef();
return (
<div className="container">
<div>
Parent Component
</div>
<button
onClick={() => { childRef.current.showAlert() }}
>
Call Function
</button>
<Child ref={childRef}/>
</div>
)
}
const Child = forwardRef((props, ref) => {
useImperativeHandle(
ref,
() => ({
showAlert() {
alert("Child Function Called")
}
}),
)
return (
<div>Child Component</div>
)
})
Class Component Solution
Child.js
import s from './Child.css';
class Child extends Component {
getAlert() {
alert('clicked');
}
render() {
return (
<h1>Hello</h1>
);
}
}
export default Child;
Parent.js
class Parent extends Component {
render() {
onClick() {
this.refs.child.getAlert();
}
return (
<div>
<Child ref="child" />
<button onClick={this.onClick}>Click</button>
</div>
);
}
}
I'm using useEffect hook to overcome the headache of doing all this so now I pass a variable down to child like this:
import { useEffect, useState } from "react";
export const ParentComponent = () => {
const [trigger, setTrigger] = useState(false);
return (
<div onClick={() => { setTrigger(trigger => !trigger); }}>
<ChildComponent trigger={trigger}></ChildComponent>
</div>
);
};
export const ChildComponent = (props) => {
const triggerInvokedFromParent = () => {
console.log('TriggerInvokedFromParent');
};
useEffect(() => {
triggerInvokedFromParent();
}, [props.trigger]);
return <span>ChildComponent</span>;
};
We can use refs in another way as-
We are going to create a Parent element, it will render a <Child/> component. As you can see, the component that will be rendered, you need to add the ref attribute and provide a name for it.
Then, the triggerChildAlert function, located in the parent class will access the refs property of the this context (when the triggerChildAlert function is triggered will access the child reference and it will has all the functions of the child element).
class Parent extends React.Component {
triggerChildAlert(){
this.refs.child.callChildMethod();
// to get child parent returned value-
// this.value = this.refs.child.callChildMethod();
// alert('Returned value- '+this.value);
}
render() {
return (
<div>
{/* Note that you need to give a value to the ref parameter, in this case child*/}
<Child ref="child" />
<button onClick={this.triggerChildAlert}>Click</button>
</div>
);
}
}
Now, the child component, as theoretically designed previously, will look like:
class Child extends React.Component {
callChildMethod() {
alert('Hello World');
// to return some value
// return this.state.someValue;
}
render() {
return (
<h1>Hello</h1>
);
}
}
Here is the source code-
Hope will help you !
If you are doing this simply because you want the Child to provide a re-usable trait to its parents, then you might consider doing that using render-props instead.
That technique actually turns the structure upside down. The Child now wraps the parent, so I have renamed it to AlertTrait below. I kept the name Parent for continuity, although it is not really a parent now.
// Use it like this:
<AlertTrait renderComponent={Parent}/>
class AlertTrait extends Component {
// You will need to bind this function, if it uses 'this'
doAlert() {
alert('clicked');
}
render() {
return this.props.renderComponent({ doAlert: this.doAlert });
}
}
class Parent extends Component {
render() {
return (
<button onClick={this.props.doAlert}>Click</button>
);
}
}
In this case, the AlertTrait provides one or more traits which it passes down as props to whatever component it was given in its renderComponent prop.
The Parent receives doAlert as a prop, and can call it when needed.
(For clarity, I called the prop renderComponent in the above example. But in the React docs linked above, they just call it render.)
The Trait component can render stuff surrounding the Parent, in its render function, but it does not render anything inside the parent. Actually it could render things inside the Parent, if it passed another prop (e.g. renderChild) to the parent, which the parent could then use during its render method.
This is somewhat different from what the OP asked for, but some people might end up here (like we did) because they wanted to create a reusable trait, and thought that a child component was a good way to do that.
For functional components easiest way is
Parent Component
parent.tsx
import React, { useEffect, useState, useRef } from "react";
import child from "../../child"
const parent: React.FunctionComponent = () => {
const childRef: any = useRef();
}
const onDropDownChange: any = (event): void => {
const target = event.target;
childRef.current.onFilterChange(target.value);
};
return <child ref={childRef} />
export default parent;
Child Component
child.tsx
import React, { useState, useEffect, forwardRef, useRef, useImperativeHandle, } from "react";
const Child = forwardRef((props, ref) => {
useImperativeHandle(ref, () => ({
onFilterChange(id) {
console.log("Value from parent", id)
},
}));
})
Child.displayName = "Child";
export default Child;
The logic is simple.
Create a function in parent using child or use ref.
I prefer the creating function in parent using child. There are multiple ways to do it.
When using functional components
In Parent
function Parent(){
const [functionToCall, createFunctionToCall] = useState(()=>()=>{})
return (
<Child createFunctionToCall={createFunctionToCall} />
)
}
In Child
function Child({createFunctionToCall}){
useEffect(()=>{
function theFunctionToCall(){
// do something like setting something
// don't forget to set dependancies properly.
}
createFunctionToCall(()=>theFunctionToCall)
},[createFunctionToCall])
}
This pattern is similar to #brickingup answer. But in this version you can set as many child actions you want.
import { useEffect } from "react";
export const Parent = () => {
const childEvents = { click: () => {} };
return (
<div onClick={() => childEvents.click()}>
<Child events={childEvents}></Child>
</div>
);
};
export const Child = (props) => {
const click = () => {
alert("click from child");
};
useEffect(() => {
if (props.events) {
props.events.click = click;
}
}, []);
return <span>Child Component</span>;
};
We're happy with a custom hook we call useCounterKey. It just sets up a counterKey, or a key that counts up from zero. The function it returns resets the key (i.e. increment). (I believe this is the most idiomatic way in React to reset a component - just bump the key.)
However this hook also works in any situation where you want to send a one-time message to the client to do something. E.g. we use it to focus a control in the child on a certain parent event - it just autofocuses anytime the key is updated. (If more props are needed they could be set prior to resetting the key so they're available when the event happens.)
This method has a bit of a learning curve b/c it's not as straightforward as a typical event handler, but it seems the most idiomatic way to handle this in React that we've found (since keys already function this way). Def open to feedback on this method but it is working well!
// Main helper hook:
export function useCounterKey() {
const [key, setKey] = useState(0);
return [key, () => setKey(prev => prev + 1)] as const;
}
Sample usages:
// Sample 1 - normal React, just reset a control by changing Key on demand
function Sample1() {
const [inputLineCounterKey, resetInputLine] = useCounterKey();
return <>
<InputLine key={inputLineCounterKey} />
<button onClick={() => resetInputLine()} />
<>;
}
// Second sample - anytime the counterKey is incremented, child calls focus() on the input
function Sample2() {
const [amountFocusCounterKey, focusAmountInput] = useCounterKey();
// ... call focusAmountInput in some hook or event handler as needed
return <WorkoutAmountInput focusCounterKey={amountFocusCounterKey} />
}
function WorkoutAmountInput(props) {
useEffect(() => {
if (counterKey > 0) {
// Don't focus initially
focusAmount();
}
}, [counterKey]);
// ...
}
(Credit to Kent Dodds for the counterKey concept.)
Parent component
import Child from './Child'
export default function Parent(props) {
const [childRefreshFunction, setChildRefreshFunction] = useState(null);
return (
<div>
<button type="button" onClick={() => {
childRefreshFunction();
}}>Refresh child</button>
<Child setRefreshFunction={(f) => {
setChildRefreshFunction(f);
}} />
</div>
)
}
Child component
export default function Child(props) {
useEffect(() => {
props.setRefreshFunction(() => refreshMe);
}, []);
function refreshMe() {
fetch('http://example.com/data.json')....
};
return (
<div>
child
</div>
)
}
You can achieve this easily in this way
Steps-
Create a boolean variable in the state in the parent class. Update this when you want to call a function.
Create a prop variable and assign the boolean variable.
From the child component access that variable using props and execute the method you want by having an if condition.
class Child extends Component {
Method=()=>{
--Your method body--
}
render() {
return (
//check whether the variable has been updated or not
if(this.props.updateMethod){
this.Method();
}
)
}
}
class Parent extends Component {
constructor(){
this.state={
callMethod:false
}
}
render() {
return (
//update state according to your requirement
this.setState({
callMethod:true
}}
<Child updateMethod={this.state.callMethod}></Child>
);
}
}
Another way of triggering a child function from parent is to make use of the componentDidUpdate function in child Component. I pass a prop triggerChildFunc from Parent to Child, which initially is null. The value changes to a function when the button is clicked and Child notice that change in componentDidUpdate and calls its own internal function.
Since prop triggerChildFunc changes to a function, we also get a callback to the Parent. If Parent don't need to know when the function is called the value triggerChildFunc could for example change from null to true instead.
const { Component } = React;
const { render } = ReactDOM;
class Parent extends Component {
state = {
triggerFunc: null
}
render() {
return (
<div>
<Child triggerChildFunc={this.state.triggerFunc} />
<button onClick={() => {
this.setState({ triggerFunc: () => alert('Callback in parent')})
}}>Click
</button>
</div>
);
}
}
class Child extends Component {
componentDidUpdate(prevProps) {
if (this.props.triggerChildFunc !== prevProps.triggerChildFunc) {
this.onParentTrigger();
}
}
onParentTrigger() {
alert('parent triggered me');
// Let's call the passed variable from parent if it's a function
if (this.props.triggerChildFunc && {}.toString.call(this.props.triggerChildFunc) === '[object Function]') {
this.props.triggerChildFunc();
}
}
render() {
return (
<h1>Hello</h1>
);
}
}
render(
<Parent />,
document.getElementById('app')
);
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.6.3/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.6.3/umd/react-dom.production.min.js"></script>
<div id='app'></div>
CodePen: https://codepen.io/calsal/pen/NWPxbJv?editors=1010
Here my demo: https://stackblitz.com/edit/react-dgz1ee?file=styles.css
I am using useEffect to call the children component's methods. I have tried with Proxy and Setter_Getter but sor far useEffect seems to be the more convenient way to call a child method from parent. To use Proxy and Setter_Getter it seems there is some subtlety to overcome first, because the element firstly rendered is an objectLike's element through the ref.current return => <div/>'s specificity.
Concerning useEffect, you can also leverage on this approach to set the parent's state depending on what you want to do with the children.
In the demo's link I have provided, you will find my full ReactJS' code with my draftwork inside's so you can appreciate the workflow of my solution.
Here I am providing you my ReactJS' snippet with the relevant code only. :
import React, {
Component,
createRef,
forwardRef,
useState,
useEffect
} from "react";
{...}
// Child component
// I am defining here a forwardRef's element to get the Child's methods from the parent
// through the ref's element.
let Child = forwardRef((props, ref) => {
// I am fetching the parent's method here
// that allows me to connect the parent and the child's components
let { validateChildren } = props;
// I am initializing the state of the children
// good if we can even leverage on the functional children's state
let initialState = {
one: "hello world",
two: () => {
console.log("I am accessing child method from parent :].");
return "child method achieve";
}
};
// useState initialization
const [componentState, setComponentState] = useState(initialState);
// useEffect will allow me to communicate with the parent
// through a lifecycle data flow
useEffect(() => {
ref.current = { componentState };
validateChildren(ref.current.componentState.two);
});
{...}
});
{...}
// Parent component
class App extends Component {
// initialize the ref inside the constructor element
constructor(props) {
super(props);
this.childRef = createRef();
}
// I am implementing a parent's method
// in child useEffect's method
validateChildren = childrenMethod => {
// access children method from parent
childrenMethod();
// or signaling children is ready
console.log("children active");
};
{...}
render(){
return (
{
// I am referencing the children
// also I am implementing the parent logic connector's function
// in the child, here => this.validateChildren's function
}
<Child ref={this.childRef} validateChildren={this.validateChildren} />
</div>
)
}
You can apply that logic very easily using your child component as a react custom hook.
How to implement it?
Your child returns a function.
Your child returns a JSON: {function, HTML, or other values} as the example.
In the example doesn't make sense to apply this logic but it is easy to see:
const {useState} = React;
//Parent
const Parent = () => {
//custome hook
const child = useChild();
return (
<div>
{child.display}
<button onClick={child.alert}>
Parent call child
</button>
{child.btn}
</div>
);
};
//Child
const useChild = () => {
const [clickCount, setClick] = React.useState(0);
{/* child button*/}
const btn = (
<button
onClick={() => {
setClick(clickCount + 1);
}}
>
Click me
</button>
);
return {
btn: btn,
//function called from parent
alert: () => {
alert("You clicked " + clickCount + " times");
},
display: <h1>{clickCount}</h1>
};
};
const rootElement = document.getElementById("root");
ReactDOM.render(<Parent />, rootElement);
<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="root"></div>
I tried using createRef or useRef. Somehow they all return null.
Secondly, this answer proposes to pass a prop that sets a function that seems the most reasonable to me. But if your child component is used in multiple places, you should add that extra prop to other places. Also if you want to call a method in the grandchild, this method might be too verbose or mouthful.
So I made my own function store in a very primitive way.
Below is functionStore.js file
const fns = {};
export function setFn(componentName, fnName, fn) {
if (fns[componentName]) {
fns[componentName][fnName] = fn;
} else {
fns[componentName] = { fnName: fn };
}
}
export function callFn(componentName, fnName) {
fns[componentName][fnName]();
}
I just set the functions that need to be called from any component.
import { setFn } from "./functionStore";
export class AComponent extends React.Component {
componentDidMount() {
setFn("AComponent", "aFunc", this.aFunc);
}
aFunc = () => { console.log("aFunc is called!"); };
}
Then I just call it from some other component
import { callFn } from "./functionStore";
export class BComponent extends React.Component {
// just call the function
bFunc = () => {
callFn("AComponent", "aFunc");
};
}
One disadvantage is the function to be called should be parameterless. But this might be fixed somehow as well. Currently, I don't need to pass parameters.
I think that the most basic way to call methods is by setting a request on the child component. Then as soon as the child handles the request, it calls a callback method to reset the request.
The reset mechanism is necessary to be able to send the same request multiple times after each other.
In parent component
In the render method of the parent:
const { request } = this.state;
return (<Child request={request} onRequestHandled={()->resetRequest()}/>);
The parent needs 2 methods, to communicate with its child in 2 directions.
sendRequest() {
const request = { param: "value" };
this.setState({ request });
}
resetRequest() {
const request = null;
this.setState({ request });
}
In child component
The child updates its internal state, copying the request from the props.
constructor(props) {
super(props);
const { request } = props;
this.state = { request };
}
static getDerivedStateFromProps(props, state) {
const { request } = props;
if (request !== state.request ) return { request };
return null;
}
Then finally it handles the request, and sends the reset to the parent:
componentDidMount() {
const { request } = this.state;
// todo handle request.
const { onRequestHandled } = this.props;
if (onRequestHandled != null) onRequestHandled();
}
Here's a bug? to look out for:
I concur with rossipedia's solution using forwardRef, useRef, useImperativeHandle
There's some misinformation online that says refs can only be created from React Class components, but you can indeed use Function Components if you use the aforementioned hooks above. A note, the hooks only worked for me after I changed the file to not use withRouter() when exporting the component. I.e. a change from
export default withRouter(TableConfig);
to instead be
export default TableConfig;
In hindsight the withRouter() is not needed for such a component anyway, but usually it doesn't hurt anything having it in. My use case is that I created a component to create a Table to handle the viewing and editing of config values, and I wanted to be able to tell this Child component to reset it's state values whenever the Parent form's Reset button was hit. UseRef() wouldn't properly get the ref or ref.current (kept on getting null) until I removed withRouter() from the file containing my child component TableConfig

React: Passing props to function components

I have a seemingly trivial question about props and function components. Basically, I have a container component which renders a Modal component upon state change which is triggered by user click on a button. The modal is a stateless function component that houses some input fields which need to connect to functions living inside the container component.
My question: How can I use the functions living inside the parent component to change state while the user is interacting with form fields inside the stateless Modal component? Am I passing down props incorrectly?
Container
export default class LookupForm extends Component {
constructor(props) {
super(props);
this.state = {
showModal: false
};
}
render() {
let close = () => this.setState({ showModal: false });
return (
... // other JSX syntax
<CreateProfile fields={this.props} show={this.state.showModal} onHide={close} />
);
}
firstNameChange(e) {
Actions.firstNameChange(e.target.value);
}
};
Function (Modal) Component
const CreateProfile = ({ fields }) => {
console.log(fields);
return (
... // other JSX syntax
<Modal.Body>
<Panel>
<div className="entry-form">
<FormGroup>
<ControlLabel>First Name</ControlLabel>
<FormControl type="text"
onChange={fields.firstNameChange} placeholder="Jane"
/>
</FormGroup>
);
};
Example: say I want to call this.firstNameChange from within the Modal component. I guess the "destructuring" syntax of passing props to a function component has got me a bit confused. i.e:
const SomeComponent = ({ someProps }) = > { // ... };
You would need to pass down each prop individually for each function that you needed to call
<CreateProfile
onFirstNameChange={this.firstNameChange}
onHide={close}
show={this.state.showModal}
/>
and then in the CreateProfile component you can either do
const CreateProfile = ({onFirstNameChange, onHide, show }) => {...}
with destructuring it will assign the matching property names/values to the passed in variables. The names just have to match with the properties
or just do
const CreateProfile = (props) => {...}
and in each place call props.onHide or whatever prop you are trying to access.
I'm using react function component
In parent component first pass the props like below shown
import React, { useState } from 'react';
import './App.css';
import Todo from './components/Todo'
function App() {
const [todos, setTodos] = useState([
{
id: 1,
title: 'This is first list'
},
{
id: 2,
title: 'This is second list'
},
{
id: 3,
title: 'This is third list'
},
]);
return (
<div className="App">
<h1></h1>
<Todo todos={todos}/> //This is how i'm passing props in parent component
</div>
);
}
export default App;
Then use the props in child component like below shown
function Todo(props) {
return (
<div>
{props.todos.map(todo => { // using props in child component and looping
return (
<h1>{todo.title}</h1>
)
})}
</div>
);
}
An addition to the above answer.
If React complains about any of your passed props being undefined, then you will need to destructure those props with default values (common if passing functions, arrays or object literals) e.g.
const CreateProfile = ({
// defined as a default function
onFirstNameChange = f => f,
onHide,
// set default as `false` since it's the passed value
show = false
}) => {...}
just do this on source component
<MyDocument selectedQuestionData = {this.state.selectedQuestionAnswer} />
then do this on destination component
const MyDocument = (props) => (
console.log(props.selectedQuestionData)
);
A variation of finalfreq's answer
You can pass some props individually and all parent props if you really want (not recommended, but sometimes convenient)
<CreateProfile
{...this.props}
show={this.state.showModal}
/>
and then in the CreateProfile component you can just do
const CreateProfile = (props) => {
and destruct props individually
const {onFirstNameChange, onHide, show }=props;

Categories