0

I'm kind of to ReactJS and I'm trying to use useContext with hooks but I'm having some trouble. I've been reading through several articles but I could not understand it.

I understand its purpose, but I can't figure out how to make it work properly. If I'm correct, the purpose is to be able to avoid passing props down to every children and be able to access values from a common provider at any depth of the component tree. This includes functions and state values. Please correct me if I'm wrong.

I've been testing with the following files. This is the ManagerContext.js file:

import { createContext } from 'react';

const fn = (t) => {
  console.log(t);
}

const ctx = createContext({
  title: 'This is a title',
  editing: false,
  fn: fn,
})

let ManagerContext = ctx;

export default ManagerContext;

Then I have the LessonManager.js file which is used in my main application:

import React from 'react';

import LessonMenu from './LessonMenu.js';

export default function LessonManager() {

    return (

             <LessonMenu />

    )

}

And finally the LessonMenu.js:

import React from 'react';

import 'rsuite/dist/styles/rsuite.min.css';

import ManagerContext from './ManagerContext.js';

export default function LessonMenu() {

    const value = React.useContext(ManagerContext);

    return (
        <div>
            <span>{value.title}</span>

            <button
                onClick={()=>value.fn('ciao')}
            >click</button>

            <button
                onClick={()=>value.title = 'new title'}
            >click</button>

        </div>
    )
}

In the LessonMenu.js file the onClick={()=>value.fn('ciao')} works but the onClick={()=>value.title = 'new title'} doesn't re render the component.

I know something is wrong, but can someone make it a bit clearer for me?

1 Answer 1

2

In order for rerendering to occur, some component somewhere must call setState. Your code doesn't do that, so no rendering happens.

The setup you've done for the ManagerContext creates a default value, but that's only going to get used if you don't render any ManagerContext.Provider in your component tree. That's what you're doing now, but it's almost certainly not what you want to. You'll want to have some component near the top of your tree render a ManagerContext.Provider. This component can will be where the state lives, and among the data it sends down will be a function or functions which set state, thus triggering rerendering:

export default function LessonManager() {
  const [title, setTitle] = useState('SomeOtherTitle');
  const [editing, setEditing] = useState(false);

  const value = useMemo(() => {
    return {
      title, 
      setTitle, 
      editing, 
      setEditing,
      log: (t) => console.log(t)
    }
  }, [title, editing]);

  return (
    <ManagerContext.Provider value={value} >
      <LessonMenu />
    </ManagerContext.Provider/>
  )
}

// used like:
export default function LessonMenu() {
  const value = React.useContext(ManagerContext);
  return (
    <div>
      <span>{value.title}</span>
      <button onClick={() => value.log('ciao')}>
        click
      </button>
      <button onClick={() => value.setTitle('new title')}>
        click
      </button>
    </div>
  )
}
Sign up to request clarification or add additional context in comments.

2 Comments

Thank you, it works now. Just one question: the [title, editing] at the end of useMemo represents the values that if changed will trigger a re render the component?
Kind of, yeah. value is an object, so if i'm not careful, i'll end up creating a new object every time LessonManager renders. This would be a problem because if i create a new object, that forces everything that uses ManagerContext to rerender to, even if the properties in side the object have not changed. So i used useMemo to create the value object just once, and then reuse the same object. The [title, editing] array says that if either title or editing change, i want the memoization to break and a new object to be created.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.