DEV Community

Cover image for React's new Context API and Actions

React's new Context API and Actions

Steven Washington on March 31, 2018

Photo: Daniel Watson Edit: 4/2/2018 - It was pointed out to me that the example in this post had a performance issue, where render was called on C...
Collapse
 
christiaanwesterbeek profile image
Christiaan Westerbeek • Edited

Thank you! I was looking for: "react handle function passed through context" and I found your post. Despite the fact that I've taken my time to understand Redux, I still find it to much bloat. Probably because "I probably don't need it". Nonetheless, I need some of its features. Your pattern hits a sweet spot. Using this as my pattern for now

import React, { Component, createContext } from 'react';
import PropTypes from 'prop-types';

const reducer = (state, action) => {
    const { type, ...rest } = action;
    if (type === 'SET_STATE') {
        return {
            ...state,
            ...rest,
        };
    }
};

// export context so that SomeComponentThatNeedContext
// can be augmented withContext using: react-context-consumer-hoc
// const EnhancedComponent = withContext(Context)(SomeComponentThatNeedContext)
export const Context = createContext();

export class Provider extends Component {
    static propTypes = {
        children: PropTypes.any,
    };
    state = {
        dispatch: action => {
            this.setState(state => reducer(state, action));
        },
    };
    render() {
        const {
            state,
            props: { children },
        } = this;
        return <Context.Provider value={state}>{children}</Context.Provider>;
    }
}

// Alternative to wrap the parent component with the Provider
export const withProvider = Component => {
    return function ComponentWithProvider(props) {
        // ... and renders the wrapped component with the Provider
        return (
            <Provider>
                <Component {...props} />
            </Provider>
        );
    };
};
Collapse
 
dan_abramov profile image
Dan Abramov

Note your example will re-render consumers more often than necessary.

reactjs.org/docs/context.html#caveats

Collapse
 
washingtonsteven profile image
Steven Washington

Thanks for pointing that out! I made some updates to that example that should address this.

always important to RTFM; I messed up in this case. 😳

Always learning!

Collapse
 
doodirock profile image
Joel Albert • Edited

I'm not too familiar with Redux so I have a few questions about this method.

1) How would I fire off multiple actions at once> For instance if my component needs to change 2-3 state values? Can I just pass multiple actions? What does that look like?

2) What if my state/action doesn't take a true/false vale but instead changes a string? For example:

state = {
msg: 'This message can change',
dispatch: action => {
this.setState(state => reducer(state, action));
}
};

How would I pass in a new msg to the action at this point to change the msg?

Thanks so much for the guidance!

Collapse
 
washingtonsteven profile image
Steven Washington

1) I would say that you could fire multiple calls to dispatch for each of your actions, or (if possible) create a new action type (a so-called "super-action") that's actually a collection of other actions.

2) Keep in mind that you action can be any sort of object. So in addition to type (which exists so you know what action you are working with), you can add in other data. An action can look like:

{
  type: "UPDATE_STRING",
  newString: "This is the new string!"
}

And then the reducer would update the state by using action.newString

Collapse
 
cyril36 profile image
cyril36

Hi,
I am using the way you are explaining in this post but i am facing an issue.

my provider state looks like :
export class CalendarProvider extends React.Component {
state = {
content: '',
dispatch: action => {
this.setState (state => CalendarReducer (state, action));
},
};

with the CalendarReducer doing :
const CalendarReducer = (state, action) => {
switch (action.type) {
case 'CONTENT': {
return Object.assign (state, {content: action.payload});
}
}
};

I would like to clone the Provider state object in my component state :
class Calendar extends Component{
//my component state = clone of the provider state
this.state = Object.assign ({}, this.props.store);
}

Now I have 2 differents object :

  • this.state (calendar State)
  • this.props.store (Calendar Provider State)

I can modify both of them doing :

  • this.state = object.assign(this.state,{content:'test'})
  • this.props.store = object.assign(this.props.store,{content:'retest'}) result : this.state ={ content:'test' } this.props.store ={ content:'retest' }

both value are changed independently.

BUT, when I am trying to do the same thing with the dispatch function, it only change the state of the provider :

this.state.dispatch({type:'CONTENT',payload:'dev'})
The result is :
this.state ={
content:'test'
}
this.props.store ={
content:'dev'
}

instead of :
this.state ={
content:'dev'
}
this.props.store ={
content:'retest'
}

The dispatch function only update the provider component.
the this.state.dispatch point to this.props.store.dispatch.

How can i do to have 2 different function :
this.state.dispatch that update this.state (the consumer)
and
this.props.store.dispatch that update this.props.store (the provider)

I hope it is clear enough
And hopefully you know where is my mistake

Thank you

Collapse
 
beratetik profile image
Berat • Edited

I just wanted to share functional component approach of above implementation.

Here you go!

codesandbox.io/s/duckify-xw555?fon...

It's my favorite one 🙌
v1

v2

Collapse
 
sartios profile image
sartios

Hello, nice post.
In my opinion, Context API is handy for simple apps. But for more complex apps, where an event-driven model would more suitable, I can't see how the Context API would solve easier the problems that Redux middlewares already do.

Collapse
 
markerikson profile image
Mark Erikson

Worth noting that I've got an open proof-of-concept PR that updates React-Redux to use the new context API.

Collapse
 
buinauskas profile image
Evaldas Buinauskas

So React now is more than just a view library?

Collapse
 
washingtonsteven profile image
Steven Washington

React has always had state management built-in. This is just another way of updating the front-end state; no assumptions are made about any back-end architecture.

In theory, instead of your action directly modifying the context state, the action could kick off a request to your backend to update its data, which then responds with updated (and server-validated) data that you can use to update your front-end store.

In that sense, React is still just a view layer, showing the data that you pass into its state and responding to user events. It's up to you to decide what sort of data updates that triggers, and React will come along for the ride. 😀

Collapse
 
erick2014 profile image
Erick Garcia • Edited

Hey, thx for your post, I was wondering how to do something similar to this, right now I'm just thinking in how to manage multiple reducers and import them at once

Collapse
 
shakilkhan12 profile image
Shakil Khan

Please tell me in the dispatch what basically doing an "action"?