I'm really new to ReactJS framework.
I'm trying to pass a callback function from parent to child component.
What i'm trying to pass is a callback function (changeState) to a child component. The child component is a fabric-ui button and this function will be called when clicking on the button.
Parent code : only the necessary code
public constructor(props: {}) {
super(props);
this.state = {
columns: [],
hasError:false,
sortedItems: []
};
this.changeState = this.changeState.bind(this);
}
public changeState = (itemId:React.ReactText)=>{
const resolvedKey='Status';
const idKey='Status';
const item = this.state.sortedItems!.filter(ite => ite[idKey] === itemId)[0];
item[resolvedKey] = 'Résolu';
}
<ResolveButton disabled={isResolved} uniqueId={fieldContent} changeState={this.changeState } />
Child code
import { PrimaryButton } from 'office-ui-fabric-react/lib/Button';
import * as React from 'react';
export interface IHandleChange {
changeState: (itemId:React.ReactText)=>void;
disabled:boolean;
uniqueId:string| number;
}
export class ResolveButton extends React.Component<IHandleChange, {}> {
constructor(props:any) {
super(props);
}
public handleClick = () => {
this.props.changeState(this.props.uniqueId);
alert(`Item ${this.props.uniqueId}`);
}
public render(): JSX.Element {
return (
<div>
{
!this.props.disabled &&
<PrimaryButton
data-automation-id="test"
text="Résolu"
onClick={this.handleClick}
allowDisabledFocus={true}
/>
}
</div>
);
}
}
export default ResolveButton;
The problem is that the child component is not even created when i execute the code above. However the child component is created when i don't pass the callback function (changestate).
This has nothing to do with the parameters disabled and uniqueId passed to the child components. what i'm trying to say is that if i try to remove changeState in the child node interface the child component is well created, however once i introduce changeState in the interface and try to pass it from the parent component the child is never shown in the DOM.
If isResolved variable is not part of the state in parent component, its change will not re-render the tree. It might be the problem.
Related
I want to call from child component function.
Where I want to call component ? Inside Parent. TO explain better
---Parent( get function from child)
---Child ( from parent call my function )
I am using class component React.
Parent component ->
constructor(props) {
super(props);
this.componentRef = React.createRef();
}
selectedColors = (colors) => {
let { theme, formValuesTheme } = this.props;
this.componentRef.current.handleChange("red");
};
<Field
id="colorDropdown"
name="colorDropdown"
component={renderColorDropdown}
label={intl.formatMessage(messages.colorTheme)}
options={options}
onChange={selectedColors}
/>
Child component ->
class SketchExample extends React.Component {
handleChange = (color) => {
console.log("I NEED BE CALLED FROM PARENT", color);
this.props.input.onChange(color.hex);
};
ERROR message is ->
TypeError: _this.componentRef.current.handleChange is not a function
I am trying to render an parent-component which has two children. The rendering of the children will switch, so one time there will be only the first child rendered, another time it will be the last and finally it will switch back to the first child (which then should contain all the values shown before).
I thought this would be simple but it turned out that it is not.
Now to the problem: Whenever the method switchContainer is called, it will switch the container and render the other. However all member-variables, props and states are getting lost and it basically reinstanciated the child-component from scratch.
Is there a way to save the child-components "as-is" and once it is getting re-rendered, it will hold all the member-variables, props and states again?
I know that you can send props and states to the element like this:
<Child props={<data>} states={<data>}>
but this doesn't solve the issue with the missing membervariables and in my opinion it isn't a smooth solution.
My attempt so far is (this is just a mockup):
class Parent extends React.Component<any,any> {
private firstContainer:any;
private secondContainer:any;
private currentContainer:any;
constructor(props:any) {
super(props);
this.firstContainer = <Child>;
this.secondContainer = <Child>;
}
public render() {
return (
<div>
{this.currentContainer}
</div>
);
}
public switchContainer() {
if(this.currentContainer === this.firstContainer) {
this.currentContainer = this.secondContainer;
}
else {
this.currentContainer = this.firstContainer;
}
this.forceUpdate();
}
}
class Child extends React.Component<any,any> {
private anyValue:string;
constructor(props) {
this.change = this.change.bind(this);
}
public render() {
return (
<input onChange={this.change} value={this.anyValue}/>
);
}
private change(e:any) {
this.anyValue = e.target.value;
}
}
You can try maintaining a state and update children in render instead of saving child as firstContainer and secondContainer
class Parent extends React.Component<any, any> {
constructor(props) {
super(props);
this.state = {
firstChild: true
};
}
public render() {
const { firstChild } = this.state;
<div>
<Child1 show={firstChild}/>
<Child2 show={!firstChild} />
</div>
}
public switchContainer() {
this.setState(({ firstChild }) => ({ firstChild: !firstChild }));
};
}
And in child component, handle show to showContent otherwise render null. If you want to retain state, you should not unmount the component.
How can I trigger a function that is inside the child component from the parent component doing it in the same style as drawer navigation.
They do it like this: this.props.navigation.toggleDrawer(); from the parent
How can I do the same?
If I understood your question correctly, I think you mixed up thing a bit. The example you are showing is an example for triggering a parent component's function from child.
I'll try to clear things up a bit with 2 examples.
1) Trigger from child:
To trigger a function of a parent component from a child you can just pass the function as a property to the child component and run it when you need it.
class Parent extends React.Component {
someFunction = (text) => {
console.log('Message from child: ', text);
}
render () {
return(
<Child someProperty={this.someFunction} />
)
}
}
class Child extends React.Component {
_onPress = () => {
// check if the property is defined and not null
if(this.props.someProperty) {
// run the function that is passed from the parent
this.props.someProperty();
}
}
render() {
return(
<Button onPress={this._onPress} title="Click Me"/>
)
}
}
2) Trigger from parent:
To trigger a function on a child component from parent, you can pass a property that changes when some action happens on the parent component. This will trigger a re-render (in most cases, for more info please take a look at shouldComponentUpdate) in child component. You can check the property changes and then do what you need to do in child component.
class Parent extends React.Component {
state = {
someParameter: 'someInitialValue',
}
someFunction = (text) => {
this.setState({ someParameter: 'someValue' });
}
render () {
return(
<Child someProperty={this.state.someParameter} />
)
}
}
class Child extends React.Component {
someFunction = (text) => {
console.log('Message from parent: ', text);
}
componentDidUpdate(prevProps, prevState, snapshot) {
// Check if the suplied props is changed
if(prevProps.someProperty !== this.props.someProperty) {
// run the function with the suplied new property
this.someFunction(this.props.someProperty);
}
}
render() {
return(
{/* ... */}
)
}
}
Since you haven't provided any code. Here are my thoughts on the problem.
When you list down a component in a navigator, be it StackNavigator or DrawerNavigator, the component will receive some props provided by the navigation class itself.
There's an option to send more parameters as props to these navigation objects. Among these extra parameters, can be your one method toggleDrawer().
Also, if you parent component is listed in the navigator and the child component is not. You'll need to explicitly pass the navigation props (this.props.navigation) to the child component.
So, when you are inside that child component, all you gotta do is fetch those props and voila, it'll do the needful!
Hope this clarifies out stuff for you.
EDIT --- For your third comment
Assumptions:
Parent component is listed as DrawerNavigator({ParentScreen: {screen: ParentScreen}})
There is a <Route/> component in ParentScreen.
So, what you can do is pass the default navigation props to the <Route> component.
Like - <Route navigation={this.props.navigation} /> and in child component, you can trigger this.props.navigation.toggleDrawer() on any onPress() event of any element.
class Parent extends React.Component {
parentFunction() {
this.refs.chid.childFunction(parameterToPassed);
}
render () {
return(
{/* ... */}
<Child ref='child' />
{/* ... */}
)
}
}
class Child extends React.Component {
childFunction(text){
console.log('parameter Passed from parent: ', text);
}
render() {
return(
{/* ... */}
)
}
}
Function named as childFunction is declared in child component. and called in parent component, inside the function parentFunction.
for more information Call child function from parent component in React Native
I'm using the react to build some input forms.
While all children inputs have and their own states to store values I have no idea how to process the to a parent.
Here's example:
class FormComponent extends Component {
constructor(props) {
super(props);
this.state = {
title: null,
someAmount: null
}
}
render() {
let me = this;
return (
<div>
<TextField
value={me.state.title}
onChange={(proxy, value) => {
me.setState({title: value})
me.hanleChnage();
}
}
/>
<TextField
value={Number.parseFloat(me.state.someAmount)}
onChange={(proxy, value) => {
if (!isNaN(Number.parseFloat(value))) {
me.setState({someAmount: value})
me.hanleChnage();
}
}
}
/>
</div>
)
}
handleChange() {
//Calling the parent
//State here is outdated
this.props.onUpdate && this.props.onUpdate(this.state);
}
}
export default FormComponent;
Or where I can find some example of usage of compex forms with much inputs in react.
Thanks!
Sounds like you need to consider moving some of your state into the parent components. The React docs have a good article about this.
To summarize, you can pass your hanleChnage(); function as a prop to your child components if you declare the function in your parent.
function handleChange() { //do something... }
...
<ChildComponent parentOnChange={this.handleChange.bind(this) />
As your components grow in complexity, you might consider using Redux for state management, thus serving as a single source for all state in your application.
Set a child property, (e.g. callParentProperty) to reference a function in the parent component (e.g. parentFunction).
class ParentComponent extends Component{
parentFunction(parameter) {
console.log("This is the form value");
console.log(parameter);
}
render() {
return <FormComponent callParentFunctionProperty={this.parentFunction.bind(this)} />
}
}
class FormComponent extends Component {
...
handleChange() {
...
let formValue = this.state.someAmount;
this.props.callParentFunctionProperty(formValue);
}
}
I've got a parent component which feeds a onSomeAction prop to a child component:
export default class myComponent extends Component {
constructor(props) {
super(props);
}
render() {
return (
<div className="mycomponent">
<ChildComponent onSomeAction={this.doSomething} />
</div>
)
}
doSomething() {
console.log(this);
}
}
In the child component when something is clicked on I'm calling a method whiih in turns calls the onSomeAction prop:
export default class ChildComponent extends Component {
render() {
return (
<div className="">
<a onClick={() => this.doIt()}>doIt</a>
</div>
)
}
doIt() {
const { onSomeAction } = this.props;
onSomeAction();
}
}
The problem I'm seeing is back in the parent component the this context seems to have been lost - the console.log in the doSomething method returns undefined. Why is this? I need to be able to access
the parent component context.
You need set context in parent component, you can do it with .bind
<ChildComponent onSomeAction={ this.doSomething.bind(this) } />
^^^^^^^^^^^^
Example
There are two options for you on how you can get the element that has been clicked or the whole component scope this.
option one:
instead of logging this you should logg the event target like so:
doSomething(e) {
console.log(e.target);
}
option two:
you have to attach the this keyword to the doSomething method like so:
constructor(props) {
super(props);
this.doSomething = this.doSomething.bind(this);
}
That was you'll be able to access the keyword this in your do something method.
Option3:
If you want to refer with this to the child component then you have to bind(this) on the function call int he child component
Actually you can fix it 3 ways:
<ChildComponent onSomeAction={ this.doSomething.bind(this) } />
<ChildComponent onSomeAction={ () => this.doSomething() } />
<ChildComponent onSomeAction={this.doSomething} />
and add this to constructor: this.doSomething = this.doSomething.bind(this)