Hi Im trying to build a todo app and trying to add todos in todo list but I get this error:
TypeError: Cannot read property 'map' of undefined
here is the error code:
4 | function TodoList() {
5 | const list = useSelector((state) => state.todoList.text);
6 | return (
> 7 | <div>
8 | {list.map((todo)=>{
9 | <li>todo</li>
10 | })}
here is the TodoList.js:
import React from "react";
import { useSelector } from "react-redux";
const TodoList = () => {
const list = useSelector((state) => state.todoList.text);
return (
<div>
{list.map((todo)=>{
<li>todo</li>
})}
</div>
);
}
export default TodoList;
here is the index.js:
import React from "react";
import ReactDOM from "react-dom";
import "./index.css";
import App from "./App";
import reportWebVitals from "./reportWebVitals";
import { Provider } from "react-redux";
import { createStore, combineReducers, applyMiddleware } from "redux";
import { TodoReducer,TodoListReducer } from "./redux/reducers";
import { composeWithDevTools } from "redux-devtools-extension";
import thunk from "redux-thunk";
const rootReducer = combineReducers({
todoList: TodoListReducer,
todos: TodoReducer
});
const middleware = [thunk];
const store = createStore(
rootReducer,
composeWithDevTools(applyMiddleware(...middleware))
);
ReactDOM.render(
<React.StrictMode>
<Provider store={store}>
<App />
</Provider>{" "}
</React.StrictMode>,
document.getElementById("root")
);
reportWebVitals();
here is the reducers.js:
import { SET_VALUE } from "./actions";
import {ADD_TODO} from "./actions"
let initial_state = [{
text:""
}]
export const TodoReducer = (state = initial_state, action) => {
switch (action.type) {
case ADD_TODO:
return [{
text: action.text,
}]
default:
return state;
}
}
export const TodoListReducer = (state = initial_state, action) => {
switch (action.type) {
case SET_VALUE:
return {
...state,
text: action.text
}
default:
return state;
}
}
Actually I'm a new coder so I might have a lot of mistakes :). Thank you for your attention.
state.todoList is an array of object. So just update like this:
const list = useSelector((state) => state.todoList);
The initial state dont have the property 'todoList' so that state.todoList is undefined.
Set a default value for it like let initial_state = {todoList:[{text:""}]}
Related
I am learning Redux, in this app I am using react-redux and redux, I am not mutating the store's state, but still my app is not re-rendering
I have this basic counter app, you press the + button the number increases, you press the - button it decreases
My code:
app.js :
`
import './App.css';
import { useDispatch } from 'react-redux';
import { store } from './store';
function App() {
const dispatch = useDispatch();
const handleIncrease = () => {
console.log("+");
dispatch({
type : 'aumentar'
})
console.log(store.getState());
}
const handleDecrease = () => {
console.log("-");
dispatch({
type : 'restar'
})
}
return (
<div className="App">
<h1>Contador</h1>
<h3>{store.getState()}</h3>
<button onClick={handleIncrease}>+</button>
<button onClick={handleDecrease}>-</button>
</div>
);
}
export default App;
`
index.js :
`
import React from 'react';
import ReactDOM from 'react-dom/client';
import './index.css';
import App from './App';
import { Provider } from 'react-redux';
import { store } from './store';
const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(
<React.StrictMode>
<Provider store={store}>
<App />
</Provider>
</React.StrictMode>
);
`
store.js
`
import { legacy_createStore as createStore } from "redux";
let initialState = 0;
const reducer = (state = initialState, action) => {
switch(action.type){
case 'aumentar' :
return state + 1;
case 'restar' :
return state - 1;
default :
return state;
}
}
export const store = createStore(reducer);
`
You need to use useSelector to access entries from the state:
// 1. import the hook
import { useDispatch, useSelector } from 'react-redux';
import { store } from './store';
function App() {
const dispatch = useDispatch();
// 2. use it to extract what you need
const count = useSelector(state => state);
const handleIncrease = () => {
console.log("+");
dispatch({
type : 'aumentar'
})
}
const handleDecrease = () => {
console.log("-");
dispatch({
type : 'restar'
})
}
// 3. use the extracted variable inside JSX
return (
<div className="App">
<h1>Contador</h1>
<h3>{count}</h3>
<button onClick={handleIncrease}>+</button>
<button onClick={handleDecrease}>-</button>
</div>
);
}
When your state will become more complex / you will use more reducers, your code will look like:
const whatYouNeed = useSelector(state => state.reducerName.something);
I am getting the error :'TypeError: store.getState is not a function' and I can't determine where the problem is
here's how I created the store:
import { applyMiddleware, createStore } from 'redux';
import thunk from 'redux-thunk';
import rootReducer from './rootReducer';
const initialState = {
pending: false,
products: [],
error: null
}
const middlewares = [thunk];
export const store=createStore(rootReducer, initialState, applyMiddleware(...middlewares));
and here's the index.js:
import React from "react";
import ReactDOM from "react-dom";
import { Provider } from "react-redux";
import store from './App'
import App from "./App";
ReactDOM.render(
<Provider store={store}>
<App />
</Provider>,
document.getElementById("root")
);
and here's the thunk function:
function fetchProducts() {
return dispatch => {
dispatch(fetchProductsPending());
fetch('https://api.spacexdata.com/v3/launches')
.then(res => res.json()
)
.then(
res => {
if(res.error) {
throw(res.error);
}
dispatch(fetchProductsSuccess(res.products));
return res.products;
})
.catch(error => {
dispatch(fetchProductsError(error));
})
}
}
export default fetchProducts;
and here's a sandbox of the problem:
https://codesandbox.io/s/polished-sunset-wxefc?file=/src/index.js
here's a screenshot of the error:
The store is not the default export from the ./App.jsx module. You either need to add curly braces to your import or export store as the default:
// App.jsx
export const store = /* ... */
// index.js
import { store } from './App';
or
// App.jsx
export default const store = /* ... */
// index.js
import store from './App';
So I'm learning react and I've split my code into seperate files which are, as follows ->ReduxDemo.js/reducers.js/store.js/actions.js
Here is their content:
ReduxDemo.js:
import React from 'react'
import {useSelector} from 'react-redux'
function ReduxDemo(){
const cakeAmount = useSelector(state=>state.cakeCount)
return(
<div>
<div>
<h1>Amount of cakes - {cakeAmount}</h1>
</div>
</div>)
}
export default ReduxDemo;
reducers.js:
import {combineReducers} from 'redux'
const cakeState=
{
cakeCount: 10,
}
const iceCreamState=
{
iceCreamCount: 20
}
const cakeReducer=(state={cakeState},action)=>
{
switch(action.type)
{
case 'buyCake':
return{
...state,
cakeCount: state.cakeCount -1
}
default:
return state
}
}
const iceCreamReducer=(state=iceCreamState, action)=>
{
switch(action.type)
{
case 'buyIceCream':
return{
...state,
iceCreamCount: state.iceCreamCount-1
}
default:
return state;
}
}
const reducers = combineReducers(
{
cake: cakeReducer,
iceCream: iceCreamReducer
})
export default reducers;
store.js:
import reducers from './reducers'
import {createStore} from 'react'
const store = createStore(reducers)
store.subscribe(()=>
{
console.log('store changed:', store.getState())
})
export default store;
actions.js:
import store from './store'
export const buyCake=()=>
{
store.dispatch({type: 'buyCake'})
}
App.js:
import React from 'react';
import logo from './logo.svg';
import './App.css';
import ReduxDemo from './ReduxDemo'
import {Provider} from 'react-redux'
import store from './store'
function App() {
return (
<div>
<Provider store={store}>
<ReduxDemo></ReduxDemo>
</Provider>
</div>
);
}
export default App;
for some obscure to me reason I get "TypeError: Object(...) is not a function", that is supposedly located at this line in store.js -> const store = createStore(reducers) Why is that and how can I fix it?
createStore is not a function offered by React. You need to import it from Redux:
// store.js
import { createStore } from 'redux'
I'm defining a variable in Page1 and would like to access it in Page2 and then when clicking back to Page1 retrieve the same variable
So far, the variable is set on Page1 but cannot be retrieved on Page2
index.js
import {createStore, applyMiddleware, combineReducers} from 'redux'
import thunk from 'redux-thunk'
import {composeWithDevTools} from 'redux-devtools-extension'
import {Provider} from 'react-redux'
import variableReducer from './reducers'
const store = createStore(
variableReducer,
composeWithDevTools(applyMiddleware(thunk))
)
ReactDOM.render(
<Provider store={store}>
<App />
</Provider>,
document.getElementById('root')
)
serviceWorker.unregister();
actions/index.js
export const SET_MY_VARIABLE = 'SET_MY_VARIABLE'
export const setMyVariable = myVariable => ({
type: SET_MY_VARIABLE,
payload: {myVariable}
})
reducers/index.js
import {SET_MY_VARIABLE} from '../actions'
const initialState = {
myVariable: ''
}
const variableReducer = (state=initialState, action) => {
switch (action.type) {
case SET_MY_VARIABLE:
return {
...state,
myVariable: action.payload.myVariable
}
default:
return state
}
}
export default variableReducer
components/Page1.js
import React, {useEffect} from 'react'
import {connect, useDispatch} from 'react-redux'
import {setMyVariable} from '../actions'
const Page1 = (props) => {
const dispatch = useDispatch()
useEffect(() => {
dispatch(setMyVariable(5000))
}, [])
return (
<div>
Setting variable<br />
Go to page 2
</div>
)
}
const mapState = state => {
return {
myVariable: state.myVariable
}
}
export default connect(mapState)(Page1)
components/Page2.js
import React from 'react'
import {connect} from 'react-redux'
const Page2 = (props) => {
const {myVariable} = props
console.log('props: ', props)
return (
<div>
Variable: {myVariable}
</div>
)
}
const mapState = state => {
console.log('map2 ', state.myVariable)
return {
myVariable: state.myVariable
}
}
export default connect(mapState)(Page2)
I should be able to set variables to the store in one component and access them throughout the entire App. Instead, I'm not able to retrieve them
Instead of using action.payload.myVariable use action.payload in your reducer/index.js
I've discovered the answer to my problem. I needed to change the <a href tag to a <Link> from react-router-dom in the Page1 component. The <a href was causing a complete reload of all JS and losing state. Here's the corrected component:
components/Page1.js
import React, {useEffect} from 'react'
import {connect, useDispatch} from 'react-redux'
import {setMyVariable} from '../actions'
import {Link} from 'react-router-dom'
const Page1 = (props) => {
const dispatch = useDispatch()
useEffect(() => {
dispatch(setMyVariable(5000))
}, [])
return (
<div>
Variable: {myVariable}<br />
<Link to="/page2">Go to page 2</Link>
</div>
)
}
const mapState = state => {
return {
myVariable: state.myVariable
}
}
export default connect(mapState)(Page1)
I've just started out with Redux and trying to implement a simple MERN App (for practice).
Everything in my code is working fine, but my reducer function is showing unexpected behaviour. When an action (which gets fetches data from express api) is called my reducer correctly goes to the particular switch case data logs successfully but then three times the default case is passed and data on my component which I log is showing null. Please Help.
Here's my code:-
index.js
import React from 'react';
import ReactDOM from 'react-dom';
import './index.css';
import App from './App';
import { BrowserRouter as Router } from 'react-router-dom';
import { Provider } from 'react-redux';
import thunk from 'redux-thunk';
import { createStore, applyMiddleware, combineReducers } from 'redux';
import articlesReducer from './store/reducers/articlesReducer';
import registerServiceWorker from './registerServiceWorker';
const rootReducer = combineReducers({
articles: articlesReducer
});
const store = createStore(rootReducer, applyMiddleware(thunk));
const app = (
<Provider store={store}>
<Router>
<App />
</Router>
</Provider>
);
ReactDOM.render(app, document.getElementById('root'));
registerServiceWorker();
App.js
import React, {Component} from 'react';
import {Switch, Route} from 'react-router-dom';
import './App.css';
import Home from './components/Home/Home';
class App extends Component {
render() {
return (
<div>
<Switch>
<Route exact path="/" component={Home} />
</Switch>
</div>
);
}
}
export default App;
articles.js
export const getAllArticles = () => {
return dispatch => {
return (
fetch('http://localhost:5000/api/articles')
.then(res => res.json())
.then(data => {
dispatch({type: 'GET_ALL_ARTICLES', articles: data})
})
);
};
};
articlesReducer.js
const initialState = {
articles:null
};
const reducer = (state = initialState, action) => {
switch (action.type) {
case 'GET_ALL_ARTICLES':
console.log('in reducer', action.type, action.articles[0]);
return {
...state,
articles: action.articles
};
default:
console.log('In default');
return state;
}
};
export default reducer;
myComponent
import React, { Component } from 'react';
import { connect } from 'react-redux';
import { getAllArticles } from '../../store/actions/articles.js';
class MainPage extends Component {
componentWillMount() {
this.props.initArticles();
console.log(this.props.articles);
}
render() {
return (
<div className="container">
<br />
<h1>Here comes the articles!!</h1>
</div>
);
}
}
const mapStateToProps = state => {
return {
articles: state.articles
};
};
const mapDispatchToProps = dispatch => {
return {
initArticles: () => dispatch(getAllArticles())
};
};
export default connect(mapStateToProps, mapDispatchToProps)(MainPage);
The output in my console is somewhat like this:-
In default
In default
In default
{articles: null}
in reducer GET_ALL_ARTICLES {articles[0] Object}
I don't know what is the mistake. Thanks for help in advance.
I'm not sure whether this is actually the problem but you incorrectly access the articles. You have a root reducer with articles reducer:
const rootReducer = combineReducers({
articles: articlesReducer
});
which initial state is:
const initialState = {
articles:null
};
And in your mapDispatchToProps you "import" whole reducer state:
const mapStateToProps = state => {
return {
articles: state.articles
};
};
I think you wanted to access articles property
const mapStateToProps = state => {
return {
articles: state.articles.articles
};
};
Other than that everything seems to be fine. I would however as pointed in comment initialize articles as empty array [].
const initialState = {
articles: []
};