Search code examples
reactjsgraphqlrelayjsrelay

Relay local state management. How to add and use flag on client side?


The problem:

I want to have simple boolean flag that will be true when modal is opened and false when it is closed. And I want to update other components reactively depends on that flag

I hope there is a way to do it with relay only (Apollo has a solution for that). I don't want to connect redux of mobx or something like that (It is just simple boolean flag!).

What I already have:

It is possible to use commitLocalUpdate in order to modify your state. Indeed I was able to create and modify my new flag like that:

class ModalComponent extends PureComponent {
    componentDidMount() {
        // Here I either create or update value if it exists
        commitLocalUpdate(environment, (store) => {  
            if (!store.get('isModalOpened')) {
                store.create('isModalOpened', 'Boolean').setValue(true);
            } else {
                store.get('isModalOpened').setValue(true);
            }
        });
    }

    componentWillUnmount() {
        // Here I mark flag as false
        commitLocalUpdate(environment, (store) => {
            store.get('isModalOpened').setValue(false);
        });
    }

    render() {
        // This is just react component so you have full picture
        return ReactDOM.createPortal(
            <div
                className={ styles.modalContainer }
            >
                dummy modal
            </div>,
            document.getElementById('modal'),
        );
    }
}

The challenge:

How to update other components reactively depends on that flag? I can't fetch my flag like this:


const MyComponent = (props) => {
    return (
    <QueryRenderer
        environment={ environment }
        query={ graphql`
            query MyComponentQuery {
                isModalOpened
            }`
        } //PROBLEM IS HERE GraphQLParser: Unknown field `isModalOpened` on type `Query`
        render={ ({ error, props: data, retry }) => {

            return (
                <div>
                    {data.isModalOpened}
                <div/>
            );
        } }
    />);
};

Because Relay compiler throws me an error: GraphQLParser: Unknown field 'isModalOpened' on type 'Query'.

And the last problem: How to avoid server request? That information is stored on client side so there is no need for request.

I know there a few maybe similar questions like that and that. But they doesn't ask most difficult part of reactive update and answers are outdated.


Solution

  • If you need to store just one flag as you said, I recommend you to use React Context instead of Relay. You could do next:

    1. Add Context to App component:
    const ModalContext = React.createContext('modal');
    
    export class App extends React.Component {
        constructor() {
            super();
            this.state = {
                isModalOpened: false
            }
        }
    
        toggleModal = (value) => {
            this.setState({
                isModalOpened: value
            })
        };
    
        getModalContextValue() {
            return {
                isModalOpened: this.state.isModalOpened,
                toggleModal: this.toggleModal
            }
        }
    
        render() {
            return (
                <ModalContext.Provider value={this.getModalContextValue()}>
                    //your child components
                </ModalContext.Provider>
            )
        }
    
    }
    
    
    1. Get value from context everywhere you want:
    const MyComponent = (props) => {
        const { isModalOpened } = useContext(ModalContext);
    
        return (
            <div>
                {isModalOpened}
            </div>
        );
    };
    

    If you will use this solution you will get rid of using additional libraries such as Relay and server requests.