Im newbie at Redux and Redux, so im trying get a simple todo list.
I've got array in the store. Its okay with adding, but when im deleting item see this:
TypeError: Cannot read property 'map' of undefined
at
var items =this.props.tasks.map((item,i) => (this.props.deleteTask(item)} />));
in ListTODO.js
index.js
import React from 'react';
import ReactDOM from 'react-dom';
import './index.css';
import App from './App';
import registerServiceWorker from './registerServiceWorker';
import { Provider } from 'react-redux';
import { createStore } from 'redux';
import reducer from './reducer';
const store = createStore(reducer);
ReactDOM.render(
<Provider store={store}>
<App className="container" />
</Provider>,
document.getElementById('root'));
registerServiceWorker();
App.js
import React, { Component } from 'react';
import InputTask from './InputTask';
import ListTODO from './ListTODO';
import { connect } from 'react-redux';
class App extends Component {
render() {
return (<div>
<strong>TODO LIST</strong>
<InputTask addTask={this.props.addTask} /><br />
<ListTODO tasks={this.props.store} deleteTask={this.props.deleteTask} />
</div>);
}
}
const mapStateToProps = state => {
return {
store: state
}
}
const mapDispatchToProps = dispatch => {
return {
addTask: (task) => {
dispatch({ type: 'UPDATE_LIST', payload: task })
},
deleteTask: (task) => {
dispatch({ type: 'DELETE_TASK', payload: task })
}
}
}
export default connect(
mapStateToProps,
mapDispatchToProps)
(App);
reducer.js
const initialState = ["task1", "task2"];
export default function todoList(state = initialState, action) {
switch (action.type) {
case 'UPDATE_LIST':
return [
...state,
action.payload
];
case 'DELETE_TASK':
console.log("DELETING", (action.payload))
return
state.filter(element => element !== action.payload);
default: return state;
}
}
ListTODO.js
import React, { Component } from 'react';
import TodoItem from './TodoItem';
import { connect } from 'react-redux';
class ListTODO extends Component {
constructor() {
super();
}
render() {
console.log("LIST TODO:"+ this.props.tasks);
var items =this.props.tasks.map((item,i) => (<TodoItem item={item} key={i} deleteTask={(item)=>this.props.deleteTask(item)} />));
return (
<ul>
{items}
</ul>
);
}
}
export default ListTODO;
Related
I have the following problem: Using the code below I get the error:
this.props.posts is undefined
As I am following the tutorial https://codeburst.io/redux-a-crud-example-abb834d763c9 an I typed everything correct I am totally confused already at the beginning of my React career. Could you please help me?
import React, { Component } from 'react';
import { connect } from 'react-redux';
import Post from './Post';
class AllPost extends Component {
render() {
return (
<div>
<h1>All Posts</h1>
{this.props.posts.map((post) => <Post key={post.id} post={post} />)}
</div>
);
}
}
const mapStateToProps = (state) => {
return {
posts: state,
}
}
export default connect(mapStateToProps)(AllPost);
Thanks a lot for your efforts!
The following is the postReducer:
const postReducer = (state = [], action) => {
switch(action.type) {
case 'ADD_POST':
return state.concat([action.data]);
default:
return state;
}
}
export default postReducer;
And here the index.js:
import React from 'react';
import ReactDOM from 'react-dom';
import './index.css';
import App from './App';
import { createStore } from 'redux';
import { Provider } from 'react-redux';
import postReducer from './reducers/postReducer';
const store = createStore(postReducer);
ReactDOM.render(
<Provider store={store}>
<App />
</Provider>, document.getElementById('root'));
You need to declare an initial state. So your this.props.posts won't be undefined.
const initialState = {
posts: []
}
const postReducer = (state = initialState, action) => {
switch(action.type) {
case 'ADD_POST':
return { ...state, posts: [...state.posts, action.data]}
default:
return state;
}
}
export default postReducer;
The second error is located inside your mapStateToProps method. You need to access to posts redux state key. posts: state.posts like I did below
import React, { Component } from 'react';
import { connect } from 'react-redux';
import Post from './Post';
class AllPost extends Component {
render() {
return (
<div>
<h1>All Posts</h1>
{this.props.posts.map((post) => <Post key={post.id} post={post} />)}
</div>
);
}
}
const mapStateToProps = (state) => {
return {
posts: state.posts, // you need to access to posts key
}
}
export default connect(mapStateToProps)(AllPost);
So, when I am trying to access my news props to display news title to my page I am receiving and error like this one:
Error Msg
import React, { Component } from 'react';
import Search from './Search';
import { connect } from 'react-redux';
import NewsItem from './NewsItem';
class NewsResults extends Component {
render() {
return (
<div>
<Search />
{this.props.news.map(item => {
return <NewsItem new={item} key={item.objectID} showButton={true}/>
})
}
</div>
)
}
}
function mapStateToProps(state) {
console.log(state)
return {
news: state.news
}
}
export default connect(mapStateToProps, null)(NewsResults);
NewsItem.js is just a component through which I will display the results on my application.
import React, { Component } from 'react';
class NewsItem extends Component {
render(){
return(
<div className="col-sm-12 col-sm-3">
<div className="thumbnail">
<div className="caption">
<h3>{this.props.new.title}</h3>
</div>
</div>
</div>
)
}
}
export default NewsItem;
This is where I'm fetching my information from an api and then I want to display on my page using maps
class Search extends Component {
constructor(props) {
super(props);
this.state = {
query: ''
};
}
search(){
console.log('Search button clicked', this.state.query);
let url = `http://hn.algolia.com/api/v1/search_by_date?query=${this.state.query}`;
// console.log(url);
fetch(url, {
method: 'GET'
}).then(response=> response.json())
.then(jsonObject => {this.props.news(jsonObject.results)});
}
This is my redux action code in action.js file
export const NEWS = "NEWS";
export function news(items) {
const action = {
type: NEWS,
items
}
return action;
}
This is the reducer file
import { NEWS } from '../actions';
const initialState = {
news: []
}
export default function (state= initialState, action) {
switch(action.type) {
case NEWS:
console.log("News are ",action.items);
return {
...state,
news: action.items
};
default:
return state;
}
};
So now I have changed my reducer to be in 2 seperate files I will post below. Also by doing that error message changed to this:
This is the new Error I am getting now
My reducer files are below:
import { NEWS } from '../actions';
export default function news(state = [], action) {
switch(action.type) {
case NEWS:
console.log("News are ",action.items);
return action.items;
default:
return state;
}
}
Second Reducer File:
import news from './news_reducer';
import { combineReducers } from 'redux';
const rootReducer = combineReducers({
news
});
export default rootReducer;
Then I am importing my rootReducer from reducer folder in index.js outside of source folder where my redux store is.
This is how I am creating my store in index.js
import React from 'react';
import ReactDOM from 'react-dom';
import './index.css';
import App from './components/App';
import registerServiceWorker from './registerServiceWorker';
import { Provider } from 'react-redux';
import { createStore } from 'redux';
import rootReducer from './reducers';
const store = createStore(rootReducer,
window.__REDUX_DEVTOOLS_EXTENSION__ && window.__REDUX_DEVTOOLS_EXTENSION__());
ReactDOM.render(
<Provider store={store}>
<App />
</Provider>
, document.getElementById('root'));
registerServiceWorker();
Now, My problem is that I am trying to send data from actions but reducer isn't recieving it for some reason.
Another Error popped up now. Another Error
I'm trying to dispatch an action, but it returns "type" of undefined. I suspect Redux Thunk is not working properly.
Before I was dispatching the same action from the parent component and it was working.
Entry point
import React, { Component } from 'react'
import { Provider } from 'react-redux'
import configureStore from '../ConfigureStore'
import '../App.css';
import App from './theapp/theAppContainer';
const store = configureStore()
class Root extends Component {
render() {
return (
<Provider store={store}>
<App />
</Provider>
)
}
}
export default Root;
Store
import { createStore, applyMiddleware } from 'redux'
import thunkMiddleware from 'redux-thunk'
import { createLogger } from 'redux-logger'
import allReducers from './reducers/index'
const loggerMiddleware = createLogger()
export default function configureStore() {
return createStore(
allReducers,
applyMiddleware(thunkMiddleware, loggerMiddleware)
)
}
The app - routing. Before I was dispatching the action at this level and it was working.
import React, { Component } from 'react'
import Cards from '../templates/cards/CardsContainer'
import EditApp from '../pages/editApp/EditApp'
import NewApp from '../pages/NewApp'
import AppReport from '../pages/AppReport'
import { Route, Switch, HashRouter } from 'react-router-dom'
export default class TheApp extends Component {
constructor(props) {
super(props)
}
render() {
const appId = window.location.href.split('id=')[1];
return (
<HashRouter>
<Switch>
<Route exact path="/" component={Cards} />
<Route path="/app" component={EditApp} />
<Route exact path="/new" component={NewApp} />
<Route path="/report" component={AppReport} />
</Switch>
</HashRouter>
)
}
}
The container where I dispatch the action
import { connect } from 'react-redux'
import Cards from './Cards'
import {
fetchAppsData
} from '../../../actions'
function mapStateToProps(state){
return {
apps: state.apps
}
}
function matchDispatchToProps(dispatch){
return dispatch(fetchAppsData)
}
export default connect(mapStateToProps, matchDispatchToProps)(Cards)
Action
import fetch from 'cross-fetch'
import * as helpers from '../Helpers';
export const REQUEST_ITEMS = 'REQUEST_ITEMS'
export const RECEIVE_ITEMS = 'RECEIVE_ITEMS'
export function fetchAppsData() {
return (dispatch) => {
return dispatch(fetchItems())
}
}
function fetchItems() {
return dispatch => {
dispatch(requestItems())
return fetch(helpers.appData)
.then(response => response.json())
.then(json => dispatch(receiveItems(json)))
}
}
function requestItems() {
return {
type: REQUEST_ITEMS
}
}
function receiveItems(json) {
return {
type: RECEIVE_ITEMS,
items: json,
receivedAt: Date.now()
}
}
The reducer
import {
REQUEST_ITEMS,
RECEIVE_ITEMS
} from '../actions/apps-actions'
export default function apps(
state = {
isFetching: false,
items: []
},
action
) {
switch (action.type) {
case REQUEST_ITEMS:
return Object.assign({}, state, {
isFetching: true
})
case RECEIVE_ITEMS:
return Object.assign({}, state, {
isFetching: false,
items: action.items
})
default:
return state
}
}
Try changing
function matchDispatchToProps(dispatch){
return dispatch(fetchAppsData)
}
Into this:
function matchDispatchToProps(dispatch){
return {
fetchAppsData: () => dispatch(fetchAppsData())
}
}
Also the function should be called “mapDispatchToProps” but that is not important for your problem.
I believe calling
dispatch(fetchAppsData)
isn't correct, fetchAppsData is a thunk creator, not a thunk directly. Instead you would want to do
dispatch(fetchAppsData())
My question is about why my Cities component in React project do not see any props. What is wrong here? Also i can't understand why reducer do not update state from axios async. What is wrong here?
Here is github link for this project: https://github.com/technoiswatchingyou/reg-form-demo
index.js
import React from 'react';
import ReactDOM from 'react-dom';
import './index.css';
import App from './App';
import registerServiceWorker from './registerServiceWorker';
import { createStore, applyMiddleware, combineReducers } from 'redux';
import { Provider } from 'react-redux';
import thunk from 'redux-thunk';
import { citiesRequestReducer } from './citiesRequestReducer';
const rootReducer = combineReducers({
cities: citiesRequestReducer
});
const store = createStore(rootReducer, applyMiddleware(thunk));
ReactDOM.render(
<Provider store={store}>
<App />
</Provider>,
document.getElementById('root')
);
registerServiceWorker();
App.js
import React, { Component } from 'react';
import './App.css';
import Cities from './cities';
class App extends Component {
render() {
return (
<div className="App">
<Cities />
</div>
);
}
}
export default App;
cities.js
import React, { Component } from 'react';
import { connect } from 'react-redux';
import { citiesRequestAction } from './citiesRequestAction';
import { bindActionCreators } from 'redux';
class Cities extends Component {
componentDidMount() {
this.props.onCitiesRequest();
console.log(this.props);
}
render() {
return (
<select className="custom-select">
{this.props.cities.map(city => (
<option key={city.id}>{city.name}</option>
))}
</select>
);
}
}
const mapStateToProps = state => ({
cities: state.cities
});
const mapDispatchToProps = dispatch => {
return bindActionCreators(
{
onCitiesRequest: citiesRequestAction
},
dispatch
);
};
export default connect(
mapStateToProps,
mapDispatchToProps
)(Cities);
citiesRequestReducer.js
import { CITIES_REQUEST } from './citiesRequestAction';
const initialState = [];
export function citiesRequestReducer(state = initialState, action) {
switch (action.type) {
case CITIES_REQUEST:
return {
...state,
cities: action.payload
};
default:
return state;
}
}
citiesRequestAction.js
import axios from 'axios';
export const CITIES_REQUEST = 'CITIES_REQUEST';
const GET_CITIES_URL = 'https://www.mocky.io/v2/5b34c0d82f00007400376066?mocky-delay=700ms';
function citiesRequest(cities) {
return {
type: CITIES_REQUEST,
payload: cities
};
}
export function citiesRequestAction() {
return dispatch => {
axios.get(GET_CITIES_URL).then(response => {
dispatch(citiesRequest(response.data.cities));
});
};
}
So problem was in citiesRequestReducer. Instead of return {...state, cities: action.payload} I just need to return action.payload.
I cant figure out what is going on. I have redux-thunk setup just like always. For some reason that I can not figure out I get the error: Error: Actions must be plain objects. Use custom middleware for async actions. can anyone help me figure this error out?
Index.js
import React from 'react';
import ReactDOM from 'react-dom';
import App from './app';
import './index.css';
ReactDOM.render(
<App />,
document.getElementById('root')
);
CreateStore.js
import { createStore, applyMiddleware } from 'redux';
import thunk from 'redux-thunk';
import reducers from './reducers';
export default function configureStore(initialState) {
return createStore(
reducers,
initialState,
applyMiddleware(thunk)
);
}
app.js
import React, { Component } from 'react';
import Routes from './Routes';
import {Provider} from 'react-redux';
import configureStore from './configureStore';
const store = configureStore();
class App extends Component {
render(){
return (
<Provider store={store}>
<Routes/>
</Provider>
);
}
}
export default App;
Today.js
import React, { Component } from 'react';
import { connect } from 'react-redux';
import { autoLocate } from '../actions';
import { Button } from './Common'
class Today extends Component {
componentDidMount() {
this.props.fetchTodayWeather('http://
api.wunderground.com/api/cc9e7fcca25e2
ded/geolookup/q/autoip.json');
}
render() {
return (
<div>
<div style={styles.Layout}>
<div><Button> HAHAH </Button></div>
<div><Button> Weather Now </Button></div>
</div>
</div>
);
}
}
const mapStateToProps = state => {
const loading = state.locate.loading;
const located = state.locate.autolocation;
return{ loading, located };
};
const mapDispatchToProps = (dispatch) => {
return {
fetchTodayWeather:(Url) => dispatch(autoLocate(Url))
};
};
export default connect(mapStateToProps,mapDispatchToProps)(Today);`
autoLocate.js
import { AUTODATA,
AUTOLOCATING
} from './types';
export const autoLocate = (url) => {
return (dispatch) => {
dispatch({ type: AUTOLOCATING });
fetch(url)
.then(data => fetchSuccess(dispatch, data))
};
};
const fetchSuccess = (dispatch, data) => {
dispatch({
type: AUTODATA,
payload: data
});
};