0
class List extends Component {
   <DoSomethingToList>
    {this.props.list.map(item => <Item key={item} ref={item} />}
   </DoSomethingToList>
}

class DoSomethingToList extends Component {

  componentDidUpdate(prevProps) {
     // I want to access refs here
     let refs = this.refs
     // refs === {} ------>   Why is this happening?
  }
   render() {
     <div>
       {this.props.children}
     </div>
   }  

}

I want to be able to access children as refs in the lifecycle methods of the wrapper component. This is so I can keep track of previous instances of domNodes for animations. But whenever I access refs, they return as an empty object. What is the React-friendly way of accessing the refs in lifecycle methods?

Jeremy Gottfried
  • 1,061
  • 13
  • 28

1 Answers1

0

The parent/container component (DoSomethingToList) should keep a track of refs.

const List = ({list}) => (
    <DoSomethingToList>
    {ctx => (
        list.map((item) => <Item key={item} ref={ctx.refs[item]} />)
    )}
    </DoSomethingToList>
)

class DoSomethingToList extends Component {
    state = {refs: []};

    componentDidUpdate(prevProps, prevState) {
        let refs = prevState.refs;
        // do something with refs...
    }

    render() {
      <div>
          {this.props.children(this.state)}
      </div>
    }  
}

I honestly is a bad practice as React would not know if any of state of element held by each ref change.

So I'd recommend exposing a callback that child component can call to let parent know of the change back to the parent.

const List = ({list}) => (
    <DoSomethingToList>
    {ctx => (
        list.map((item) => 
            <Item key={item} value={ctx.values[item]} onUpdate={ctx.updateValue} />)
    )}
    </DoSomethingToList>
)

class DoSomethingToList extends Component {
    state = { 
        values: [],
        updateValue: this.updateValue
    };

    updateValue = (value, i) => {
        const {values} = prevState;
        const newValues = {...values};
        newValues[i] = value;
        this.setState({values: newValues});
    }

    componentDidUpdate(prevProps, prevState) {
        const {values} = prevState;
        // do something with values...
    }
    render() {
    <div>
        {this.props.children(this.state)}
    </div>
    }  
}
dance2die
  • 35,807
  • 39
  • 131
  • 194
  • Thank you. In your solution, is `state.values` actually holding the child refs, or is it just holding props containing the item value? – Jeremy Gottfried Oct 24 '18 at 20:34
  • Also, is onUpdate a valid React event? I've never seen that used before – Jeremy Gottfried Oct 24 '18 at 20:38
  • The recommended way is not to use `refs` at all so `state.values` would contain states to manipulate child components not refs. And `onUpdate`, I used it as a random prop name as `Item` looked like a custom component. You could name it however you want if `Item` is your own implementation – dance2die Oct 24 '18 at 20:51