Search code examples
reactjstypescriptreact-context

Cannot pass data to child component with Context React


I am storing some data in my parent component to an array of objects. After that I am storing it in Context in order to pass it to another component. So my parent looks like this:

    export type HistoryData = {
          input: string;
          date: string;
        };
        
        type Props = {
          children?: ReactNode;
        };
        
        type ContextProviderType = {
          history: HistoryData[];
          setHistory: React.Dispatch<React.SetStateAction<HistoryData[]>>;
        };
        
        const values = {} as HistoryData;
        
        export const Context = createContext<ContextProviderType>({
          history: [],
          setHistory: () => {},
        });
        
        export const Overview = ({ children }: Props) => {
          const [inputText, setInputText] = useState('');
          const [data, setData] = useState<any[]>([]);
          const [loading, setLoading] = useState(false);
          const [history, setHistory] = useState<HistoryData[]>([]);
        
          const handleSubmit = (e: React.MouseEvent<HTMLElement>) => {
            e.preventDefault();
            setLoading(false);
            axios
              .get(`https://api.github.com/users/${inputText}/repos`)
              .then((response) => {
                setData(response.data);
              })
              .catch((error) => {
                console.log('error', error);
                setLoading(true);
              });
            const clonedValues = { ...values };
            clonedValues.input = inputText;
            clonedValues.date = new Date().toLocaleString();
            setHistory([...history, clonedValues]);
          };
          console.log(history);
      <Context.Provider value={{ history, setHistory }}>
    ....
</Context.Provider>
     );
    };
    
    export const useContextData = () => useContext(Context);

So basically, after each input i am storing the input and the exact date of the input while it clicked, and later i want to pass it to the other component like that:

import { useContextData } from './Overview';

    const History = () => {
      const { history, setHistory } = useContextData();
      console.log(history);

but all I get is an empty history array [].

Just to clarify, the History component is another page with /history url... I am not sure maybe that re-renders and clears the array??

My App.tsx:

function App() {
  return (
    <>
      <Routes>
        <Route path="/" element={<Overview />} />
        <Route path="/history" element={<History />} />
      </Routes>
    </>
  );
}

export default App;

And my index.tsx:

const root = ReactDOM.createRoot(
  document.getElementById('root') as HTMLElement
);
root.render(
  <React.StrictMode>
    <BrowserRouter>
      <App />
    </BrowserRouter>
  </React.StrictMode>
);

Any help would be appreciated! :)


Solution

  • In order to share a context, two components must be descendents of the same Provider. That means that you need to have the context provider as a parent to your components.

    Export Context and useContextData into another file.

    In your App.tsx file you will need to wrap the routes in the context provider:

    function App() {
      const [history, setHistory] = useState<HistoryData[]>([]);
      return (
        <Context.Provider value={{ history, setHistory }}>
          <Routes>
            <Route path="/" element={<Overview />} />
            <Route path="/history" element={<History />} />
          </Routes>
        </Context.Provider>
      );
    }
    

    Then in each of the files you would access the history and setHistory props using context:

       const History = () => {
          const { history, setHistory } = useContextData();
    
       export const Overview = ({ children }: Props) => {
          const { history, setHistory } = useContextData();
    

    This should solve the issue.