简体   繁体   中英

How do I access refs of a parent component in the child

I have a parent and a child, I have to make sure that the child component that is contained within the father can refer to him.

For example, on the child button I want to call the openDrawer() method, which is used in the parent.

I tried to pass the parent's reference to the child through a props, but it does not work.

Where am I doing wrong?

How can I do?

Parent:

import * as React from 'react';
import { StyleSheet, View, Text, ToastAndroid, Button, Toolbar } from 'react-native';
import NavigationDrawerLayout from 'react-native-navigation-drawer-layout';

import Homepage from './page/Homepage';
import PageOne from './page/pageOne';
import PageTwo from './page/pageTwo';

export default class App extends React.Component {
  constructor() {
    super();
    this.state = {
      menu: '',
      type: '',
      drawer: ''
    };
  }

  render() {
    return (
      <NavigationDrawerLayout
        ref={_drawer => (this.drawer = _drawer)}
        //ref={(_drawer) => this.setState({drawer:_drawer})}
        percent={75}
        //statusBar="#008cff"
        //statusBarTransparency={0.3}
        type={this.state.type}
        drawerPosition="left"
        selected="opt3"
        window="menu"
        color="#fff"
        backgroundColor="#303030" //303030
        imageBackground="https://c.wallhere.com/photos/aa/44/glare_colorful_bright_circles-679384.jpg!d"
        first={'username'}
        second={'joined'}
        account={[
          {
            username: 'james.bond',
            name: 'James Bond',
            email: 'james.bond.xx@xxx.xxx',
            image:
              'https://cdn1.iconfinder.com/data/icons/avatar-2-2/512/Casual_Man_2-512.png',
            joined: 'Joined at Jun 21, 2021',
            badgeText: '100',
            badgeColor: '#fff',
            badgeBackground: '#303030',
            circle: ['transparent', 'transparent'],
          },
          {
            username: 'sherlock.holmes',
            name: 'Sherlock Holmes',
            email: 'sherlock.holmes.xx@xxx.xxx',
            badgeText: '100',
            badgeColor: '#fff',
            badgeBackground: '#303030',
            circle: ['#fff000', 'transparent', '#00ffd0'],
          },
          {
            name: 'Shinichi Kudo',
            email: 'shinichi.kudo.xx@xxx.xxx',
            image:
              'https://cdn1.iconfinder.com/data/icons/avatar-2-2/512/Casual_Man_3-512.png',
            badgeText: '21',
            badgeColor: '#fff',
            badgeBackground: '#25dbd2',
            joined: 'Joined at Jun 31, 2021',
            circle: ['transparent', 'transparent'],
          },
          {
            name: 'Arthur Conan Doyle',
            email: 'arthur.conan.doyle.xx@xxx.xxx',
            image:
              'https://cdn0.iconfinder.com/data/icons/user-interface-vol-3-12/66/68-512.png',
            circle: ['transparent', 'transparent'],
          },
        ]}
        badgeFunction={e => {
          return e > 99 ? '99+' : e;
        }}
        menu={[
          {
            type: 'menu',
            name: 'opt0',
            backgroundLarge: 'transparent',
            backgroundLargeFocus: 'transparent',
            backgroundSmall: 'rgba(13, 71, 161, 0.5)',
            backgroundSmallFocus: 'rgba(213, 0, 0, 0.5)',
            iconLeft: 'apps',
            iconLeftColor: '#c1c1c1',
            iconLeftColorFocus: '#4CAF50',
            title: 'Le mie app e i miei giochi',
            titleColor: '#000000',
            titleColorFocus: '#4CAF50',
            badgeText: '100',
            badgeColor: '#ffffff',
            badgeBackground: '#1194ff',
            iconRight: 'exit-to-app',
            iconRightColor: '#4CAF50',
            iconRightColorFocus: '#EF6C00',
            close: false,
          },
          {
            type: 'menu',
            name: 'opt3',
            title: 'Abbonamenti',
            backgroundLarge: '#4286f4',
            backgroundLargeFocus: '#34ed6b',
            backgroundSmallFocus: 'rgba(213, 0, 0, 0.5)',
            iconLeft: 'apps',
            iconLeftColor: '#c1c1c1',
            iconLeftColorFocus: '#4CAF50',
            badgeText: '100',
            badgeColor: '#ffffff',
            badgeBackground: '#1194ff',
            iconRight: 'exit-to-app',
            iconRightColor: '#4CAF50',
            iconRightColorFocus: '#EF6C00',
          },
        ]}
        onPress={e => {
          ToastAndroid.show("Title: "+e.title+" * "+"Name: "+e.name,ToastAndroid.SHORT);
          console.log('Menu:', e);
          var type = e.name == 'opt2' ? 'simple' : '';
          this.setState({ menu: e.title, type });
        }}>
        <Homepage drawer={this.drawer} />
      </NavigationDrawerLayout>
    );
  }
}

const styles = StyleSheet.create({});

Child:

import * as React from 'react';
import { Text, View, Button } from 'react-native';

export default class Homepage extends React.Component {
  render() {
    const {drawer} = this.props;
    return (
      <View
        style={{
          flex: 1,
          //alignItems: 'flex-end',
        }}>
        <Text style={{ marginTop: 25, marginRight: 5, textAlign: 'right' }}>Hello World!</Text>
        <Text style={{ marginTop: 25, marginRight: 5, textAlign: 'right' }}>
          State: !
        </Text>
        <Button
          onPress={()=drawer.openDrawer()}
          title="Open"
          color="#4286f4"
        />
      </View>
    );
  }
}

Usually a child shouldn't be aware of a parent. In this case it's not parent ( App ) ref that is passed but App 's another child ref, this.drawer .

The problem here is that deprecated ref function is used, ref={_drawer => (this.drawer = _drawer)} . It results in race condition, since this.drawer is passed to Homepage before it was assigned with a ref, it's undefined.

It should be:

this.drawerRef = React.createRef();
...

<NavigationDrawerLayout ref={this.drawerRef} .../>
...
<Homepage drawerRef={this.drawerRef} />

As soon as NavigationDrawerLayout is mounted, drawerRef.current is assigned with an instance and can be used:

<Button onPress={()=drawerRef.current.openDrawer()} />

Passing refs breaks encapsulation and provides tight coupling where it could be avoided. Homepage doesn't really need the access to entire drawer. This is usually solved in React by passing function prop instead:

<Homepage openDrawer={() => this.drawerRef.current.openDrawer()} />

Solution 1

You can pass related method directly to your child. I prefer this.

import * as React from 'react';
import { StyleSheet, View, Text, ToastAndroid, Button, Toolbar } from 'react-native';
import NavigationDrawerLayout from 'react-native-navigation-drawer-layout';

import Homepage from './page/Homepage';
import PageOne from './page/pageOne';
import PageTwo from './page/pageTwo';

export default class App extends React.Component {
  constructor() {
    super();
    this.state = {
      menu: '',
      type: '',
      drawer: ''
    };
    this.openDrawer = this.openDrawer.bind(this);
  }

  openDrawer() {
    // ...
  }

  render() {
    return (
      <NavigationDrawerLayout
        ref={_drawer => (this.drawer = _drawer)}
        //ref={(_drawer) => this.setState({drawer:_drawer})}
        percent={75}
        //statusBar="#008cff"
        //statusBarTransparency={0.3}
        type={this.state.type}
        drawerPosition="left"
        selected="opt3"
        window="menu"
        color="#fff"
        backgroundColor="#303030" //303030
        imageBackground="https://c.wallhere.com/photos/aa/44/glare_colorful_bright_circles-679384.jpg!d"
        first={'username'}
        second={'joined'}
        account={[
          {
            username: 'james.bond',
            name: 'James Bond',
            email: 'james.bond.xx@xxx.xxx',
            image:
              'https://cdn1.iconfinder.com/data/icons/avatar-2-2/512/Casual_Man_2-512.png',
            joined: 'Joined at Jun 21, 2021',
            badgeText: '100',
            badgeColor: '#fff',
            badgeBackground: '#303030',
            circle: ['transparent', 'transparent'],
          },
          {
            username: 'sherlock.holmes',
            name: 'Sherlock Holmes',
            email: 'sherlock.holmes.xx@xxx.xxx',
            badgeText: '100',
            badgeColor: '#fff',
            badgeBackground: '#303030',
            circle: ['#fff000', 'transparent', '#00ffd0'],
          },
          {
            name: 'Shinichi Kudo',
            email: 'shinichi.kudo.xx@xxx.xxx',
            image:
              'https://cdn1.iconfinder.com/data/icons/avatar-2-2/512/Casual_Man_3-512.png',
            badgeText: '21',
            badgeColor: '#fff',
            badgeBackground: '#25dbd2',
            joined: 'Joined at Jun 31, 2021',
            circle: ['transparent', 'transparent'],
          },
          {
            name: 'Arthur Conan Doyle',
            email: 'arthur.conan.doyle.xx@xxx.xxx',
            image:
              'https://cdn0.iconfinder.com/data/icons/user-interface-vol-3-12/66/68-512.png',
            circle: ['transparent', 'transparent'],
          },
        ]}
        badgeFunction={e => {
          return e > 99 ? '99+' : e;
        }}
        menu={[
          {
            type: 'menu',
            name: 'opt0',
            backgroundLarge: 'transparent',
            backgroundLargeFocus: 'transparent',
            backgroundSmall: 'rgba(13, 71, 161, 0.5)',
            backgroundSmallFocus: 'rgba(213, 0, 0, 0.5)',
            iconLeft: 'apps',
            iconLeftColor: '#c1c1c1',
            iconLeftColorFocus: '#4CAF50',
            title: 'Le mie app e i miei giochi',
            titleColor: '#000000',
            titleColorFocus: '#4CAF50',
            badgeText: '100',
            badgeColor: '#ffffff',
            badgeBackground: '#1194ff',
            iconRight: 'exit-to-app',
            iconRightColor: '#4CAF50',
            iconRightColorFocus: '#EF6C00',
            close: false,
          },
          {
            type: 'menu',
            name: 'opt3',
            title: 'Abbonamenti',
            backgroundLarge: '#4286f4',
            backgroundLargeFocus: '#34ed6b',
            backgroundSmallFocus: 'rgba(213, 0, 0, 0.5)',
            iconLeft: 'apps',
            iconLeftColor: '#c1c1c1',
            iconLeftColorFocus: '#4CAF50',
            badgeText: '100',
            badgeColor: '#ffffff',
            badgeBackground: '#1194ff',
            iconRight: 'exit-to-app',
            iconRightColor: '#4CAF50',
            iconRightColorFocus: '#EF6C00',
          },
        ]}
        onPress={e => {
          ToastAndroid.show("Title: "+e.title+" * "+"Name: "+e.name,ToastAndroid.SHORT);
          console.log('Menu:', e);
          var type = e.name == 'opt2' ? 'simple' : '';
          this.setState({ menu: e.title, type });
        }}>
        <Homepage openDrawer={this.openDrawer} />
      </NavigationDrawerLayout>
    );
  }
}

const styles = StyleSheet.create({});

Solution 2

You can use directly React.createRef() .

URL: https://reactjs.org/docs/refs-and-the-dom.html

import * as React from 'react';
import { StyleSheet, View, Text, ToastAndroid, Button, Toolbar } from 'react-native';
import NavigationDrawerLayout from 'react-native-navigation-drawer-layout';

import Homepage from './page/Homepage';
import PageOne from './page/pageOne';
import PageTwo from './page/pageTwo';

export default class App extends React.Component {
  constructor() {
    super();
    this.state = {
      menu: '',
      type: '',
      drawer: ''
    };
    this.myRef = React.createRef();
  }

  render() {
    return (
      <NavigationDrawerLayout
        ref={this.myRef}
        //ref={(_drawer) => this.setState({drawer:_drawer})}
        percent={75}
        //statusBar="#008cff"
        //statusBarTransparency={0.3}
        type={this.state.type}
        drawerPosition="left"
        selected="opt3"
        window="menu"
        color="#fff"
        backgroundColor="#303030" //303030
        imageBackground="https://c.wallhere.com/photos/aa/44/glare_colorful_bright_circles-679384.jpg!d"
        first={'username'}
        second={'joined'}
        account={[
          {
            username: 'james.bond',
            name: 'James Bond',
            email: 'james.bond.xx@xxx.xxx',
            image:
              'https://cdn1.iconfinder.com/data/icons/avatar-2-2/512/Casual_Man_2-512.png',
            joined: 'Joined at Jun 21, 2021',
            badgeText: '100',
            badgeColor: '#fff',
            badgeBackground: '#303030',
            circle: ['transparent', 'transparent'],
          },
          {
            username: 'sherlock.holmes',
            name: 'Sherlock Holmes',
            email: 'sherlock.holmes.xx@xxx.xxx',
            badgeText: '100',
            badgeColor: '#fff',
            badgeBackground: '#303030',
            circle: ['#fff000', 'transparent', '#00ffd0'],
          },
          {
            name: 'Shinichi Kudo',
            email: 'shinichi.kudo.xx@xxx.xxx',
            image:
              'https://cdn1.iconfinder.com/data/icons/avatar-2-2/512/Casual_Man_3-512.png',
            badgeText: '21',
            badgeColor: '#fff',
            badgeBackground: '#25dbd2',
            joined: 'Joined at Jun 31, 2021',
            circle: ['transparent', 'transparent'],
          },
          {
            name: 'Arthur Conan Doyle',
            email: 'arthur.conan.doyle.xx@xxx.xxx',
            image:
              'https://cdn0.iconfinder.com/data/icons/user-interface-vol-3-12/66/68-512.png',
            circle: ['transparent', 'transparent'],
          },
        ]}
        badgeFunction={e => {
          return e > 99 ? '99+' : e;
        }}
        menu={[
          {
            type: 'menu',
            name: 'opt0',
            backgroundLarge: 'transparent',
            backgroundLargeFocus: 'transparent',
            backgroundSmall: 'rgba(13, 71, 161, 0.5)',
            backgroundSmallFocus: 'rgba(213, 0, 0, 0.5)',
            iconLeft: 'apps',
            iconLeftColor: '#c1c1c1',
            iconLeftColorFocus: '#4CAF50',
            title: 'Le mie app e i miei giochi',
            titleColor: '#000000',
            titleColorFocus: '#4CAF50',
            badgeText: '100',
            badgeColor: '#ffffff',
            badgeBackground: '#1194ff',
            iconRight: 'exit-to-app',
            iconRightColor: '#4CAF50',
            iconRightColorFocus: '#EF6C00',
            close: false,
          },
          {
            type: 'menu',
            name: 'opt3',
            title: 'Abbonamenti',
            backgroundLarge: '#4286f4',
            backgroundLargeFocus: '#34ed6b',
            backgroundSmallFocus: 'rgba(213, 0, 0, 0.5)',
            iconLeft: 'apps',
            iconLeftColor: '#c1c1c1',
            iconLeftColorFocus: '#4CAF50',
            badgeText: '100',
            badgeColor: '#ffffff',
            badgeBackground: '#1194ff',
            iconRight: 'exit-to-app',
            iconRightColor: '#4CAF50',
            iconRightColorFocus: '#EF6C00',
          },
        ]}
        onPress={e => {
          ToastAndroid.show("Title: "+e.title+" * "+"Name: "+e.name,ToastAndroid.SHORT);
          console.log('Menu:', e);
          var type = e.name == 'opt2' ? 'simple' : '';
          this.setState({ menu: e.title, type });
        }}>
        <Homepage drawer={this.myRef.current} />
      </NavigationDrawerLayout>
    );
  }
}

const styles = StyleSheet.create({});

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM