Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

useContext example from docs not working. #406

Open
Brentably opened this issue Dec 15, 2024 · 3 comments
Open

useContext example from docs not working. #406

Brentably opened this issue Dec 15, 2024 · 3 comments

Comments

@Brentably
Copy link

Brentably commented Dec 15, 2024

import { observer, useObservable } from "@legendapp/state/react";

const StateContext = createContext();

function App() {
  const state$ = useObservable({
    profile: {
      name: "",
    },
  });

  return (
    <StateContext.Provider value={state$}>
      <div>
        <Sidebar />
        <Main />
      </div>
    </StateContext.Provider>
  );
}

const Sidebar = function Sidebar() {
  // StateContext will never change so this will never cause a render
  const state$ = useContext(StateContext);

  // This component never re-renders, but name re-renders itself
  return (
    <div>
      Name: <Memo>{state$.profile.name}</Memo>
    </div>
  );
};

Was really excited to try this out, unfortunately, immediately get
Screenshot 2024-12-14 at 18 53 39
This error, because no argument is put into createContext. I've tried setting it to null, tried setting it to an observable of the initial state. Nothing works. Here is my _app.tsx file:

import type { AppProps } from "next/app";
import { GlobalStoreProvider } from '../context/GlobalStoreContext';
import { observable, Observable } from '@legendapp/state';
import { useObservable } from '@legendapp/state/react';
import React, { createContext, useContext, useReducer, ReactNode } from 'react';

// Define the shape of your global state
interface GlobalState {
  user: string | null;
  counter: number; // Add counter to the global state
}
// Initial state
const initialState: GlobalState = {
  user: null,
  counter: 0, // Initialize counter
};

// Create context
const GlobalStoreContext = createContext()
// Custom hook to use the global store
export const useGlobalStore = () => useContext(GlobalStoreContext); 


export default function App({ Component, pageProps }: AppProps) {
  const state$ = useObservable(initialState)

  return (
    <GlobalStoreContext.Provider value={state$}>
      <Component {...pageProps} />
    </GlobalStoreContext.Provider>
  );
}

Another one that I get a lot if I try to useObserver is:

    at App (./pages/_app.tsx:30:89)
 ⨯ TypeError: Cannot read properties of null (reading 'useRef')
    at process.env.NODE_ENV.exports.useRef (/Users/brentburdick/Dev/brick/brick-sent/brick-v2-frontend/node_modules/@legendapp/state/node_modules/react/cjs/react.development.js:1497:33)
    at useObservable (node_modules/@legendapp/state/react.mjs:439:15)
    at initialState (pages/_app.tsx:26:31)```
    
    Please advise :/ 
@EnterNameHere7
Copy link

EnterNameHere7 commented Dec 30, 2024

I've encountered the same issue but was able to fix it with the following:

const initialisedState: StateI = {
    workout: {
        started: false
    }
}

export const StateContext = createContext<ObservableObject<StateI> | null>(null)

export const AppContextProvider = ({children}: ContextBaseProps) => {

    const state$ = useObservable(initialisedState)

    return (
        <StateContext.Provider value={state$}>
            {children}
        </StateContext.Provider>
    );
};

export type StateI = {
    workout: WorkoutI
}

export type WorkoutI = {
    started: boolean
}

export type ContextBaseProps = {
    children: ReactNode | ReactNode[];
};

In your child you just do the following:

const state$ = useContext(StateContext)

if (state$ !== null) {
 observe(() => {
     if (state$.workout.started.get()) {
         console.log("started)
     }
 })
}

@jmeistrich
Copy link
Contributor

It seems like the solution to the useContext issue is to update the docs to fix the types? I think this would be the change with the correct types:

interface UserState {
    profile: {
        name: string;
    };
}
const StateContext = createContext<Observable<UserState>>(undefined as any);

function App() {
  const state$ = useObservable({
    profile: {
      name: "",
    },
  })

  return (
    <StateContext.Provider value={state$}>
      <div>
        <Sidebar />
        <Main />
      </div>
    </StateContext.Provider>
  )
}

@jmeistrich
Copy link
Contributor

@Brentably If there's a problem with useObserve or useObservable can you please create a separate issue for that? I'm not sure how the error log you posted is even possible... Can you share a larger example?

@EnterNameHere7 I think your context example would be very problematic. Putting an observe in a React component would create a new observe on every render and never clean them up. The useObserve hook manages all of the React lifecycle to make sure that's not a problem.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants