sarora
sarora

Reputation: 519

ReactJS overriding or editing context along the hierarchy

I know that in ReactJS, "context" can be used to pass down data from a component to its ancestors. However, can this context be modified along the hierarchy? If yes, how is this behavior described?

For instance: let's say that components are nested as follows: (A -> B -> C) component B is a child of component A, and component C is a child of component B. If A passes down some data through context, it can be accessed likewise from B and C. However, can B modify this context before it is passed down to C?

Upvotes: 10

Views: 4939

Answers (2)

Soreine
Soreine

Reputation: 121

Yes, any component can extend the context being passed down for its children by providing a context object itself. The context provided by the component will be merged with the context provided by its parents. This means that keys in the context object can be added or overridden at will.

Example

Consider the hierarchy of components:

<A>
  <B>
    <C/>
  </B>
</A>

With the following contexts provided:

class A extends React.Component {

    ...

    getChildContext() {
        return {
            x: 'a',
            y: 'a'
        };
    }

    ...

}

class B extends React.Component {

    ...

    getChildContext() {
        return {
            y: 'b',
            z: 'b'
        };
    }

    ...

}

Then C will receive the following context object:

{
    x: 'a', // Provided by A
    y: 'b', // Provided by A and B, overriden by B's value
    z: 'b'  // Provided by B
}

Upvotes: 8

sarora
sarora

Reputation: 519

Yes. Consider the following two examples, each involving three nested components. Both of these examples use the following HTML file:

File - index.html

<!DOCTYPE html>
<html>
  <head>
    <title>Hello React</title>
    <!-- Not present in the tutorial. Just for basic styling. -->
    <link rel="stylesheet" href="css/base.css" />
    <script src="https://cdnjs.cloudflare.com/ajax/libs/react/0.13.0/react.js"></script>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/react/0.13.0/JSXTransformer.js"></script>
  </head>
  <body>
    <div id="content"></div>
    <script type="text/jsx" src="components/ParentComponent.jsx"></script>
    <script type="text/jsx" src="components/ChildComponent.jsx"></script>
    <script type="text/jsx" src="components/GrandchildComponent.jsx"></script>

    <script type="text/jsx">
      React.render(<ParentComponent />, document.getElementById('content'));
    </script>
  </body>
</html>

EXAMPLE 1:

File - ParentComponent.jsx

var ParentComponent = React.createClass({

    render: function () {
        console.log("context in render method of ParentComponent: ");
        for(var propName in this.context)
        {
            console.log(propName + ": " + this.context[propName]);
        }

        return (
            <div>
                <h1>Text displayed by ParentComponent</h1>
                <ChildComponent />
            </div>
            );
    },

    //for sending to descendants
    childContextTypes: {
        styleA: React.PropTypes.object,
    },

    getChildContext() {
        return {
            styleA: customStyleA
        };
    }
});

var customStyleA = {
    color: 'blue'
};

File - ChildComponent.jsx

var ChildComponent = React.createClass({
    contextTypes: {
        styleA: React.PropTypes.object
    },

    render: function () {

        console.log("context in render method of ChildComponent: ");
        for(var propName in this.context)
        {
            console.log(propName + ": " + this.context[propName]);
        }

        return (
            <div>
                <h1 style={this.context.styleA}>Text displayed by ChildComponent</h1>
                <GrandchildComponent />
            </div>
            );
    },

    //for sending to descendants
    childContextTypes: {
        styleB: React.PropTypes.object,
    },

    getChildContext() {
        return {
            styleB: customStyleB
        };
    }
});

var customStyleB = {
    color: 'red'
};

File - GrandchildComponent.jsx

var GrandchildComponent = React.createClass({
    contextTypes: {
        styleA: React.PropTypes.object,
        styleB: React.PropTypes.object
    },

    render: function () {

        console.log("context in render method of GrandchildComponent: ");
        for(var propName in this.context)
        {
            console.log(propName + ": " + this.context[propName]);
        }

        return (
            <h1 style={this.context.styleB}>Text displayed by GrandchildComponent</h1>
            );
    }
});

Upon running this example, this is the output in the console:

context in render method of ParentComponent:
context in render method of ChildComponent: 
styleA: [object Object]
context in render method of GrandchildComponent: 
styleA: [object Object]
styleB: [object Object]

As can be seen, in this case, ChildComponent added a new key:value pair to the context object. Hence, the object returned by the getChildContext() function of ChildComponent got ADDED to the context.

In the output, the first line of text is black, the second line of text is blue, and the final line of text is red.

EXAMPLE 2

File - ParentComponent.jsx

var ParentComponent = React.createClass({

    render: function () {
        console.log("context in render method of ParentComponent: ");
        for(var propName in this.context)
        {
            console.log(propName + ": " + this.context[propName]);
        }

        return (
            <div>
                <h1>Text displayed by ParentComponent</h1>
                <ChildComponent />
            </div>
            );
    },

    //for sending to descendants
    childContextTypes: {
        styleA: React.PropTypes.object,
    },

    getChildContext() {
        return {
            styleA: customStyleA
        };
    }
});

var customStyleA = {
    color: 'blue'
};

File - ChildComponent.jsx

var ChildComponent = React.createClass({
    contextTypes: {
        styleA: React.PropTypes.object
    },

    render: function () {

        console.log("context in render method of ChildComponent: ");
        for(var propName in this.context)
        {
            console.log(propName + ": " + this.context[propName]);
        }

        return (
            <div>
                <h1 style={this.context.styleA}>Text displayed by ChildComponent</h1>
                <GrandchildComponent />
            </div>
            );
    },

    //for sending to descendants
    childContextTypes: {
        styleA: React.PropTypes.object,
    },

    getChildContext() {
        return {
            styleA: customStyleB
        };
    }
});

var customStyleB = {
    color: 'red'
};

File - GrandchildComponent.jsx

var GrandchildComponent = React.createClass({
    contextTypes: {
        styleA: React.PropTypes.object,
        styleB: React.PropTypes.object
    },

    render: function () {

        console.log("context in render method of GrandchildComponent: ");
        for(var propName in this.context)
        {
            console.log(propName + ": " + this.context[propName]);
        }

        return (
            <h1 style={this.context.styleA}>Text displayed by GrandchildComponent</h1>
            );
    }
});

Upon running this example, this is the output in the console:

context in render method of ParentComponent: 
context in render method of ChildComponent: 
styleA: [object Object]
context in render method of GrandchildComponent: 
styleA: [object Object]
styleB: undefined

As can be seen, in this case, ChildComponent tried to add a new key:value pair to the context. However, since the key already existed, its existing value got replaced by the new value supplied by ChildComponent. Essentially, the new value OVERRODE the previous value.

The output is the same as the first example. So in general, it can be inferred that the context can be modified by components along the hierarchy chain. Moreover, if this modification involves an existing key, the value corresponding to that key is overridden (replaced). Otherwise, the new key:value pairs are simply added to the context object.

Upvotes: 10

Related Questions