Using React context API with react grafana plugins

I am trying to use React context API using react hooks. I am unable to use my own defined context states in child components of plugin.

Please suggest example or any solutions.

Can you provide more information about what you are working on? @marcusolsson might be able to help you if you can explain more clearly what you are trying to do.

I have developed grafana plugin which is developed using react panel type as panel.
Since in react app we have feasibility to use context api for state management and data sharing between parent to child components to avoid props drilling.

I have a created a simple context in plugin source code tried to wrap the same using react hooks in GraphPanel Component. Created a child component to consume context data and events.

Source code for PluginContext.tsx
import React, { createContext, useState } from ‘react’;

const PanelContext = createContext({

userDetails: {firstName: "", lastName: ""},

onSave: (_firstName: string, _lastName: string) => {},

onTest: () => {}

});

export const PanelProvider: React.FC = ( {children} ) => {

const [userDetails, setUserDetails] = useState({firstName: "", lastName: ""});

const onSave = (firstName: string, lastName: string) => {

    console.log("Inside PanelContext FirstName", firstName);

    console.log("Inside PanelContext LastName", lastName);

    setUserDetails({ firstName, lastName });

}

const onTest = () => {

    console.log("I am on test task");

}

return (

    <PanelContext.Provider value={{userDetails, onSave, onTest}}>

        {children}

    </PanelContext.Provider>

)

}

export default PanelContext;

Source code for GraphPanel.tsx

import React, { useState, useContext } from ‘react’;

import { PanelProps } from ‘@grafana/data’;

import PanelContext, { PanelProvider } from ‘PluginContext’;

import DetailsPanelComponent from ‘DetailsPanelComponent’;

export interface GraphPanelOptions {

graphType: string;

}

export const GraphPanel: React.FC<PanelProps> = props => {

const [firstName, setFirstName] = useState(’’);

const [lastName, setLastName] = useState(’’);

const { onTest } = useContext(PanelContext);

// useEffect(() => {

// console.log(‘test me in plugin’, props);

// }, []);

const onChangeFirstName = (event: any) => {

setFirstName(event.target.value);

};

const onChangeLastName = (event: any) => {

setLastName(event.target.value);

};

const onSubmit = () => {

// console.log('Inside onSubmit firstName', firstName);

// console.log('Inside onSubmit lastName', lastName);

// onSave(firstName, lastName);

onTest();

};

return (

<PanelProvider>

  <div style={{ display: 'flex' }}>

    <div style={{ display: 'inline-block' }}>

      <div style={{ margin: '10px' }}>

        <label htmlFor="first_name" style={{ marginRight: '10px' }}>

          First Name

        </label>

        <input

          type="text"

          onChange={onChangeFirstName}

          value={firstName}

          id="first_name"

          style={{ border: '1px solid white' }}

        ></input>

      </div>

      <div style={{ margin: '10px' }}>

        <label htmlFor="last_name" style={{ marginRight: '10px' }}>

          Last Name

        </label>

        <input

          type="text"

          onChange={onChangeLastName}

          value={lastName}

          id="last_name"

          style={{ border: '1px solid white' }}

        ></input>

      </div>

      <div style={{ display: 'flex', margin: '10px', justifyContent: 'flex-end' }}>

        <button type="button" onClick={onSubmit}>

          Save

        </button>

      </div>

    </div>

    <div>

      <DetailsPanelComponent />

    </div>

  </div>

</PanelProvider>

);

};

Source code for child component DetailsPanelComponent.tsx

import React, { useContext } from ‘react’;

import PanelContext from ‘PluginContext’;

const DetailsPanelComponent = () => {

const { userDetails } = useContext(PanelContext);

return (

    <div>

        <label>First Name : {userDetails.firstName}</label>

        <label>Last Name : {userDetails.lastName}</label>

    </div>

)

}

export default DetailsPanelComponent;

@dianapayton @marcusolsson Need your kind attention to this problem.

@khalidbasapur I took a look at the code examples you posted and put together an example on stackblitz to try to help you resolve the issue you’re having. Stackblitz panel context example

To sum up the issue you’re having… the context provider needs to be lifted up one level for the PanelContext.value to be available where you are trying to use it.

Hopefully this example and explanation helps you. Let me know if not and I’ll try to explain better.

1 Like

@jackwestbrook Thank you for the example. With React App I Understand we need to wrap root app within PanelContext. But grafana plugin provides module.tsx file where PanelPlugin is constructed with custom panel component. Please let me know how to lift up the level in grafana react framework.

module.ts
import { PanelPlugin } from ‘@grafana/data’;

import { GraphPanel, GraphPanelOptions } from ‘./GraphPanel’;

export const plugin = new PanelPlugin(GraphPanel).setPanelOptions(builder => {

return builder.addRadio({

path: 'graphType',

defaultValue: 'line',

name: 'Graph Type',

settings: {

  options: [

    {

      value: 'line',

      label: 'Line',

    },

    {

      value: 'bar',

      label: 'Bar',

    },

  ],

},

});

});