I'm simply trying to dispatch an action to the store and am getting this error. I have no idea what listener2 is or why there's a TypeError:
listener2 is not a function
TypeError: listener2 is not a function
at wrappedListener
at Map.forEach (<anonymous>)
at Object.dispatch
at dispatch
at App
This is my only slice:
import {createSlice} from '@reduxjs/toolkit';
const PostContainerSlice = createSlice({
name: "postContainer",
initialState: [],
reducers: {
addPost: (state, action) => {
state.push(action.payload)
},
}
})
export const {addPost} = PostContainerSlice.actions;
export default PostContainerSlice.reducer;
store.js:
import {configureStore} from '@reduxjs/toolkit';
import PostContainerReducer from './features/post/PostContainerSlice.js';
export default configureStore({
reducer: {
postContainer: PostContainerReducer
}
})
index.js:
import React from 'react';
import ReactDOM from 'react-dom/client';
import './index.css';
import App from './App';
import store from './store.js';
const root = ReactDOM.createRoot(document.getElementById('root')).render(
<React.StrictMode>
<App dispatch={store.dispatch} state={store.getState()} />
</React.StrictMode>
);
store.subscribe(root);
App.js:
import './App.css';
import PostContainer from './features/post/PostContainer'
function App(props) {
const {state, dispatch} = props;
dispatch({
type: 'postContainer/addPost',
payload: 'test'
})
console.log(state)
}
export default App;
Console logging the store's state works fine, just dispatching is causing this. When removing 'store.subscribe(root)' from index.js the error doesn't happen, but console logging shows that nothing was dispatched and the store remains unchanged...maybe that has nothing to do with the issue. Also, when dispatching and logging state from index.js above the App rendering, the store appears to dispatch and log normally... thought this was a clue but has led me nowhere.
The "listener" in question here is the listener callback function that store.subscribe
expects to be passed.
ReactDOM.createRoot().render
doesn't return a function.
const root = ReactDOM.createRoot(document.getElementById('root')).render(
<React.StrictMode>
<App dispatch={store.dispatch} state={store.getState()} />
</React.StrictMode>
);
store.subscribe(root); // root is not a function
You might be able to get by with creating a function that renders the app that can function as a listener callback as well.
Example:
const root = ReactDOM.createRoot(document.getElementById('root'));
const renderApp = () => {
root.render(
<React.StrictMode>
<App dispatch={store.dispatch} state={store.getState()} />
</React.StrictMode>
);
};
renderApp(); // <-- call once to initially render app
store.subscribe(renderApp); // <-- pass as listener to rerender with state changes
Note: This is not how we use Redux in React though!
You should import and wrap the App
component in the react-redux
Provider
component to provide the Redux store context to the React app.
import React from 'react';
import ReactDOM from 'react-dom/client';
import { Provider } from 'react-redux';
import './index.css';
import App from './App';
import store from './store.js';
const root = ReactDOM.createRoot(document.getElementById('root')).render(
<React.StrictMode>
<Provider store={store}>
<App />
</Provider>
</React.StrictMode>
);
The App
component, and all components in the sub-ReactTree, should import and use the useDispatch
and useSelector
hooks from react-redux
to dispatch actions to the store, and subscribe to selected state updates.
import { useEffect } from 'react';
import { useDispatch, useSelector } from 'react-redux';
import './App.css';
import PostContainer from './features/post/PostContainer'
function App() {
const dispatch = useDispatch();
const postContainer = useSelector(state => state.postContainer);
useEffect(() => {
dispatch({
type: 'postContainer/addPost',
payload: 'test'
});
}, [dispatch]);
useEffect(() => {
console.log({ postContainer });
}, [postContainer]);
...
}
export default App;