问题
I'm able to fetch the weather by a given city but can't figure how to delete it afterwards. Being still new to Redux I'm trying to figure which pieces am I missing on this puzzle.
I'm leaving the FETCH_WEATHER code in just for context proposes. My issues is with the DELETE_CITY implementation.
Bug:
Steps:
Add a button in each row that calls an action creator.
Action creator takes the name of the 'city' and return an action of type 'DELETE_CITY'.
Reducer, watches for that type and iterates through the array of cities and deletes the object with the given city name.
action creator
import axios from 'axios';
const API_KEY = '1111111111111111111111';
const ROOT_URL = `http://api.openweathermap.org/data/2.5/forecast?appid=${API_KEY}`;
export const FETCH_WEATHER = 'FETCH_WEATHER';
export const DELETE_CITY = 'DELETE_CITY';
//fetch cities
export function fetchWeather(city){
const url = `${ROOT_URL}&q=${city},&units=imperial`;
const request = axios.get(url);
console.log('Request:', request);
return {
type: FETCH_WEATHER,
payload: request
}
}
//Delete Cities
export function deleteCity(city) {
return {
type: DELETE_CITY,
payload: city
}
}
Reducer: index.js
import { combineReducers } from 'redux';
import WeatherReducer from './reducer_weather';
const rootReducer = combineReducers({
weather: WeatherReducer
});
export default rootReducer;
weather_reducer.js
Is the case for DELETE_CITY implemented accurately?
import { FETCH_WEATHER } from '../actions/index';
import { DELETE_CITY } from '../actions/index';
export default function (state = [], action) {
console.log('Action received', action);
//only fetch weather data type
switch (action.type) {
case FETCH_WEATHER:
//concat to prevent state mutation
return state.concat([action.payload.data]);
case DELETE_CITY:
return state
.slice(0, action.payload.data)
.concat([action.payload.data].slice([action.payload.data] + 1));
}
return state;
}
- Add a button in each row that calls an action creator.
Ps:Are mapStateToProps and mapDispatchToProps correct?
weather_container.js
import React, { Component } from 'react';
import { connect } from 'react-redux';
import Chart from '../components/chart';
import GoogleMap from '../components/google_map';
import { bindActionCreators } from 'redux';
import { deleteCity } from '../actions/index';
class WeatherList extends Component {
renderWeather(cityData) {
const name = cityData.city.name;
return (
<tr key={name}>
<td><button onClick={() => this.props.deleteCity(cityData)} className="btn btn-danger">x</button></td>
</tr>
)
}
render() {
return (
<table>
<thead>
<tr>
<th>City</th>
</tr>
</thead>
<tbody>
{this.props.weather.map(this.renderWeather)}
</tbody>
</table>
)
}
}
function mapStateToProps(state) {
return {
weather: state.weather
}
}
function mapDispatchToProps(dispatch, weather) {
return bindActionCreators({deleteCity},{weather}, dispatch)
}
export default connect(mapStateToProps, mapDispatchToProps)(WeatherList);
I need to bind the action creator to the onClick event. I tried both methods: fat arrow functions and/or 'binding the method inside of a constructor()'.
They both return with props undefined.
回答1:
Try replaceing:
{this.props.weather.map(this.renderWeather)}
With
{this.props.weather.map(this.renderWeather, this)}
The second argument of map is the thisArg - value to use as this when executing callback.
回答2:
You need to bind renderWeather function.
class WeatherList extends Component {
constructor(){
super();
this.renderWeather = this.renderWeather.bind(this);
}
...
}
回答3:
The error message you're getting cannot read property props of undefined means that in this.props this is undefined.
It's a common gotcha in react, mainly because this is falling out of context.
What you need to do is bind the renderWeather function to WeatherList in the constructor
constructor() {
this.renderWeather = this.renderWeather.bind(this)
}
Another alternative is to use arrow functions
class {
renderWeather = (weatherData) => {
...
}
...
}
You might also consider this implementation in you reducer when deleting a city.
[...state].filter((weather) => {
return weather.id != action.payload.data.id
})
.filter() returns a new array of items that meet the given condition
来源:https://stackoverflow.com/questions/45286460/redux-call-to-action-creator-returns-cannot-read-property-props-of-undefined