IRonny
IRonny

Reputation: 131

Why I have got an empty object from my context?

I have a context and try to pass it to my children components:

const PatientContext = createContext([{}]);

function App() {
  const [selectedPatient, setSelectedPatient] = useState([]);
  const [patients, setPatients] = useState([{ name: 'Taras', age: 19 }, { name: 'Taras', age: 19 }, { name: 'Taras', age: 19 }]);

  async function patientsHasChanged() {
    const patientService = new PatientService();
    const loadedPatients = await patientService.getPatients();
    setPatients(loadedPatients);
  };

  return (
    <div className='app'>
      <PatientContext.Provider value={[selectedPatient, setSelectedPatient, patients, patientsHasChanged]}>
        <Sidebar />
        <Content />
      </PatientContext.Provider>
    </div>
  );
}

But if I try use it in my child components I am always getting empty object, how i can fix it? img with my problem here

UPD: Here's my component with using context:

const Sidebar = (props) => {
    const [patients, selectedPatient, setSelectedPatient] = useContext(PatientContext);

    return (
        <div className='sidebar'>
            <div className='search'>
                <input type="text" placeholder='Search' />
                <a href="/TechTask/new"><button>New patient</button></a>
            </div>
            <div className='list'>
                <ul>
                    {patients.map((p) =>
                        <li onClick={setSelectedPatient}>
                            <div className='name'>
                                {p.name}
                            </div>
                            <div className='age'>
                                {p.age}
                            </div>
                        </li>)
                    }
                </ul>
            </div>
        </div>
    );
};

Upvotes: 0

Views: 215

Answers (1)

AKX
AKX

Reputation: 168913

Your problem is you're using an array ([]) for the context; arrays have a given order. You pass in [selectedPatient, setSelectedPatient, patients, patientsHasChanged], and you're unpacking things in a different order ([patients, selectedPatient, setSelectedPatient]), so you get the "wrong" objects.

Instead, use an object ({}) so you can unpack it as you like, and remember to memoize the context value to avoid unnecessary rerenders.

const PatientContext = React.createContext(null);

const Sidebar = (props) => {
  const { patients, selectedPatient, setSelectedPatient } = React.useContext(PatientContext);

  return (
    <div className="sidebar">
      <div className="search">
        <input type="text" placeholder="Search" />
        <a href="/TechTask/new">
          <button>New patient</button>
        </a>
      </div>
      <div className="list">
        <ul>
          {patients.map((p) => (
            <li onClick={setSelectedPatient}>
              <div className="name">{p.name}</div>
              <div className="age">{p.age}</div>
            </li>
          ))}
        </ul>
      </div>
    </div>
  );
};

function App() {
  const [selectedPatient, setSelectedPatient] = React.useState([]);
  const [patients, setPatients] = React.useState([
    { name: "Taras", age: 19 },
    { name: "Taras", age: 19 },
    { name: "Taras", age: 19 },
  ]);

  function patientsHasChanged() {/* elided since SO doesn't support async */}

  const patientContext = React.useMemo(() => ({ selectedPatient, setSelectedPatient, patients, patientsHasChanged }), [
    selectedPatient,
    setSelectedPatient,
    patients,
    patientsHasChanged,
  ]);

  return (
    <div className="app">
      <PatientContext.Provider value={patientContext}>
        <Sidebar />
      </PatientContext.Provider>
    </div>
  );
}

ReactDOM.render(<App />, document.getElementById("root"));
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/17.0.0/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/17.0.0/umd/react-dom.production.min.js"></script>
<div id="root"></div>

Upvotes: 2

Related Questions