简体   繁体   中英

Is passing the “this” context through props an anti-pattern?

I have two components, a parent and a child like so:

class Parent extends React.Component {
    shuffle() {
        ...
    }
    blur() {
        ...
    }
    next() {
        ...
    }
    previous() {
        ...
    }
    render() {
        return (
            <Child Parent={this} />
        );
    }
}

class Child extends React.Component {
    constructor(props) {
        super();

        this.state = {};

        this._onShuffleClick = this._onShuffleClick.bind(props.Parent);
        this._onPreviousClick = this._onPreviousClick.bind(props.Parent);
        this._onNextClick = this._onNextClick.bind(props.Parent);
    }
    _onShuffleClick(event) {
        event.preventDefault();

        this.shuffled ? this.shuffle(false) : this.shuffle(true); // I can call parents method here as the 'this' context is the 'Parent'.
        this.blur(event.target);
        this.setState({test "test"}); //I can set the parents state here
    }
    _onPreviousClick(event) {
        event.preventDefault();

        this.previous();
        this.blur(event.target);
    }
    _onNextClick(event) {
        event.preventDefault();

        this.next();
        this.blur(event.target);
    }
    render() {
        return (
            <a className="shuffle" key={1} onClick={this._shuffleOnClick}>{this.props.Parent.props.html.shuffle}</a>,
            <a className="previous" key={2} onClick={this._previousOnClick}>{this.props.Parent.props.html.previous}</a>,
            <a className="next" key={3} onClick={this._nextOnClick}>{this.props.Parent.props.html.next}</a>,
        );
    }
}

Is passing the context ('this' keyword) as a prop an anti-pattern? Is setting the state of the parent from the child bad?

If I do this I then don't have to pass a lot of individual props to the child and I can also set the state of the parent from the child.

You can interact with the state of a parent from a child-component, but probably not the way you are trying to achieve this.

If you want to send in all props of the parent down to a child, you can do:

<Child {...this.props} />

This way, you don't need to specify each individual prop one at a time; instead, you just send them all in. Check out the spread operator here and here for more info. More info also on MDN:

The spread syntax allows an expression to be expanded in places where multiple arguments (for function calls) or multiple elements (for array literals) or multiple variables (for destructuring assignment) are expected.


If you want to access or modify the state of a parent from a child you have to do this slightly differently. Typically, you would create a function that does this interaction with the state in your parent and then send that function as a prop down to the child. Like this:

Parent:

_modifyState = (bar) => {
  this.setState({foo: bar});
}
.....
<Child modifyState={this._modifyState} />

Child:

this.props.modifyState("Hello world!");

The above will set state.foo in the parent to the string Hello world! from the child component.

If you want access to all state variables, you could send it in as a prop to the child (the whole object) and then have a function (like above) which modifies the entire state (not just one property) - depends what you want really.

Well, it's mainly a bad usage of passing around the props, you could also go for {...props} instead, and I wouldn't want to pass it through the full name, you can also use let { props } = this; let parentProps = props.Parent.props let { props } = this; let parentProps = props.Parent.props . The question is also, why would you refer to parent props, that seems the bad practise, divide and conquor, only pass the props that are really needed, and do not assume in your child components that a certain parent component is available

When you pass event handlers down, let those eventhandlers be bound to your current this, but don't bind them in the child to an expected parent, a bit like this example

 var StyledButton = React.createClass({ propTypes: { clickHandler: React.PropTypes.func.Required, text: React.PropTypes.string.required }, render: function() { let { clickHandler, text } = this.props; return <button type="button" onClick={clickHandler}>{text}</button>; } }); var MyForm = React.createClass({ click: function() { alert('ouch'); }, render: function() { return <fieldset> <StyledButton clickHandler={this.click} text="Click me" /> </fieldset> } }) ReactDOM.render( <MyForm />, document.getElementById('container') ); 
 <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="container"> <!-- This element's contents will be replaced with your component. --> </div> 

Yes I do think your code is bad practice. Now you chid components know about the parent component which makes your child impure.

When your parent implementation changes, the child components will break because of this.props.Parent.props.html.previous} .

I think each react component should update the parent by calling the parents functions passed by the props.

class Parent extends React.Component {

    doSomethingBeacauseTheChildStateHasChanged() {
        // function
    }

    render() {
        <Child doSomething={doSomethingBeacauseTheChildStateHasChanged.bind(this)}/>
    }
}

class Child extends React.Component {

    render() {
        <button onClick={this.props.doSomething}>Child button</button>
    }

}

Note: I am not an expert and React beginner, treat this as an opinion rather than guideline.

I think yes cause you force particular implementation. What would you do if you wanted to have those methods in GrandParent? If you use props this modification is really easy, but with your implementation it would be pain in the ass.

There is also a feature called PropTypes . It's really great to make components reusable, but it's yet another thing you can't use if you do the things like you have proposed.

Maybe it is just me but this also creates a great confusion. You should pass everything you need as props.

Also setting parent state like this

this.setState({test "test"}); //I can set the parents state here

seems bad to me. I would rather pass a function from parent as a prop and bind parent before passing it down.

You can trigger a function in the Parent. This is the correct way to a children communicates with its parent.

class Parent extends React.Component {
  shuffle(e) {
    console.log(e.target);
    return false;
  }
  render() {
    return (
        <Child onShuffle={this.shuffle} />
    );
  }
}

class Child extends React.Component {
  render() {
    return(
      <a href='#' onClick={this.props.onShuffle}>Shuffle</a>
    );
  }
}
Child.propTypes = {
  onShuffle: React.PropTypes.func
}

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