2

I have a problem that I just can't solve in React. My intention is to define a button callback that will set the value of a state in redux, this callback uses the dispatch function within it with two values containing two different redux states. It seems that these instructions

//Saving select selected value from redux store
const selectedValue: string|undefined = useSelector((state: RootState) => state.selectedOption);

//2. Save the input value from the redux store
const inputValue: string|null = useSelector((state: RootState) => state.inputTextValue);

are executed before the actual initialization of the store in redux so react gives me this problem ---> "Could not find react-redux context value; please ensure the component is wrapped in a "

I tried to define it like this:

App.tsx

import React, { useCallback } from 'react';
import './App.css';
import { Card } from './components/organism/Card';
import { useState } from "react";
import { Context } from './context/Context'
import { imgData } from "./context/Context";
import { Provider, useDispatch, useSelector } from "react-redux";
import StudentExercise from "./components/organism/studentExercise/studentExercise";
import { createStore } from "redux";
import rootReducer from "./redux";
import { composeWithDevTools } from "redux-devtools-extension";
import { optionsArray } from "./redux/reducers/SelectedOption/selectedOption";
import {
  setBirthYear,
  setCourse,
  setName,
  setSurname,
  setTutor
} from "./redux/actions";

export type RootState = ReturnType<typeof store.getState>

// todo: cercare metodo non deprecato
const store = createStore(rootReducer, composeWithDevTools());// Reducer store

function App() {
  // States
  const [value, setValue] = useState('My context value');
  const [imageData, setImageData] = useState(imgData); 

  // Callbacks:
  const dispatch = useDispatch();

  // Saving selected value from redux store 
  const selectedValue: string|undefined = useSelector((state: RootState) => state.selectedOption);

  // 2. Saving another value from redux store
  const inputValue: string|null = useSelector((state: RootState) => state.inputTextValue);

  // Callbacks:
  const handleClick = useCallback(()=> {
    switch(selectedValue) {
      case "name":
        console.log("Selected value --> ",selectedValue);
        console.log(inputValue);
        return dispatch(setName(inputValue));
      case "surname":
        return dispatch(setSurname(inputValue));
      case "birthYear":
        return dispatch(setBirthYear(inputValue));
      case "course":
        return dispatch(setCourse(inputValue));
      case "tutor":
        return dispatch(setTutor(inputValue));
      default:
        return selectedValue;
    }
  }, [dispatch]);

  return (
    <>
      <div className="App">
        <Provider store={store}> {/* Redux store */}
          <Context.Provider value={{ value, setValue }}> {/* Esercizio card */}
            <Card />
          </Context.Provider>

          <StudentExercise 
            clickHandler={handleClick} 
            title={"changing values!"}
          />
        </Provider>
      </div>
    </>
  );
}

export default App;

My expectation is to have a callback defined in the outermost possible component, in this case App so that I can reuse the StudentExercise component but with handleClick passed as prop.

Drew Reese
  • 165,259
  • 14
  • 153
  • 181

1 Answers1

0

The issue is that this App component is the component rendering the Redux Provider component and providing the store to its sub-ReactTree. This means App itself cannot access the store since there's not any Redux provider higher in the ReactTree.

Move the Provider component higher in the ReactTree so that App can access the redux store.

Example:

import React, { useCallback, useState } from 'react';
import { useDispatch, useSelector } from "react-redux";
import './App.css';
import { Card } from './components/organism/Card';
import { Context } from './context/Context';
import { imgData } from "./context/Context";
import StudentExercise from "./components/organism/studentExercise/studentExercise";
import { optionsArray } from "./redux/reducers/SelectedOption/selectedOption";
import {
    setBirthYear,
    setCourse,
    setName,
    setSurname,
    setTutor
} from "./redux/actions";
import type { RootState } from './index.tsx';

...

function App() {
  const [value, setValue] = useState('My context value');
  const [imageData, setImageData] = useState(imgData);
   
  const dispatch = useDispatch();

  const selectedValue: string|undefined = useSelector((state: RootState) => state.selectedOption);
  const inputValue: string|null = useSelector((state: RootState) => state.inputTextValue);

  const handleClick = useCallback(()=> {
    switch(selectedValue) {
      case "name":
        return dispatch(setName(inputValue));
      case "surname":
        return dispatch(setSurname(inputValue));
      case "birthYear":
        return dispatch(setBirthYear(inputValue));
      case "course":
        return dispatch(setCourse(inputValue));
      case "tutor":
        return dispatch(setTutor(inputValue));
      default:
        return selectedValue;
    }
  }, [dispatch]);

  return (
    <div className="App">
      <Context.Provider value={{ value, setValue }}>
        <Card />
      </Context.Provider>
      <StudentExercise 
        clickHandler={handleClick} 
        title="changing values!"
      />
    </div>
  );
}

export default App;
import { Provider } from "react-redux";
import {createStore} from "redux";
import rootReducer from "./redux";
import { composeWithDevTools } from "redux-devtools-extension";

export type RootState = ReturnType<typeof store.getState>

const store = createStore(rootReducer, composeWithDevTools());

...

<Provider store={store}>
  <App />
</Provider>
Drew Reese
  • 165,259
  • 14
  • 153
  • 181