I am trying to create a basic Redux use-case to share state between 2 sibling components (SimpleFruit
and FruitLabel
). I cannot figure out why it keeps running into the error: Cannot read properties of undefined
. Since I am setting the initial state to a string, it should not run into the undefined state issue AFAIK.
Would really like to know what I am doing wrong.
P.S. I have created the project using npx create-react-app --template redux
as suggested by the official docs, so configuration should work fine.
EDIT: As explained by Nils Hartmann in his comment below, I needed to use useSelector()
on my components. Additionally the dispatch needed to be passed through a callback for immutability. The code below has been updated with the changes
app/store.js:
import { configureStore } from '@reduxjs/toolkit';
import fruitReducer from '../features/fruits/fruitSlice';
export const store = configureStore({
reducer: {
fruit: fruitReducer,
},
});
State/Action/Reducer: features/fruits/fruitSlice.js
import { createSlice } from '@reduxjs/toolkit';
const initialState = {
fruitname: 'pineapple',
};
export const fruitSlice = createSlice({
name: 'fruit',
initialState,
reducers: {
setFruit: (state, action) => {
state.fruitname = action.payload;
}
}
})
export const selectFruit = (state) => state.fruit.fruitname;
export const { setFruit } = fruitSlice.actions;
export default fruitSlice.reducer;
Component 1: features/fruits/SimpleFruit.js
import React from 'react';
import { selectFruit, setFruit } from './fruitSlice';
//import { useDispatch } from 'react-redux'; //PREVIOUS
import { useSelector, useDispatch } from 'react-redux'; //NEW
export function SimpleFruit(props) {
// Parameters
const {seller} = props;
// Store state
//const fruit = selectFruit(); //PREVIOUS
const fruit = useSelector(selectFruit); //NEW
const dispatch = useDispatch();
// Callbacks (NEW)
const handleChange = (e) => {
dispatch(setFruit(e.target.value)); // needed for setting value
}
// Render
return(
<div>
<p>SIMPLE FRUIT by <b>{seller}</b></p>
<label htmlFor="fname">Fruit Name:</label>
<input
type="text"
id="fname"
name="fname"
value={fruit}
onChange={handleChange}
/>
</div>
);
}
Component 2: features/fruits/FruitLabel.js
import React from 'react';
import { selectFruit } from './fruitSlice';
import { useSelector } from 'react-redux'; //NEW
export function FruitLabel(props) {
// Store state
//const fruit = selectFruit(); //PREVIOUS
const fruit = useSelector(selectFruit); //NEW
// Render
return(
<h1>FRUIT IS {fruit}</h1>
);
}
and finally, the main app: App.js:
import React from 'react';
import './App.css';
// Custom
import { SimpleFruit } from './features/fruits/SimpleFruit';
import { FruitLabel } from './features/fruits/FruitLabel';
function App() {
return (
<div className="App">
<SimpleFruit seller="John Doe" />
<FruitLabel />
</div>
);
}
export default App;
CodePudding user response:
You need to pass selectFruit
to useSelector:
import { useSelector } from 'react-redux';
// ...
export function SimpleFruit(props) {
const fruit = useSelector(selectFruit);
// ...
}