Хорошо, много твиков, но подведем итог:
- Все
this.setState()
вызовы должны выполняться в методе класса. connect()
требует 2 параметра: mapStateToProps
и mapDispatchToProps
... если не используется mapStateToProps
, вы должны передать null
в качестве первого параметра. - Ваши имена для ввода формы были повсюду.Будьте последовательны:
<input name="name" value={this.state.name} onChange={this.handleChange} />
.Использование ввода id
s не требуется и не рекомендуется.В этом случае this.handleChange
использует e.target.name
и e.target.value
для отслеживания входных данных и их значений. - Убедитесь, что все входные данные были заполнены, прежде чем разрешить пользователю отправлять.
- Избегайте использования жирных стрелок в методе рендеринга, например:
onChange={ (e) => this.handleChange(e) }
, потому что они будут дублироваться при каждом повторном рендеринге компонента.Иногда их нельзя избежать, но они направлены на минимизацию использования. - Проверьте крайние случаи (например, когда
details
пусто), в противном случае ваше приложение будет аварийно завершать работу при попытке map
неопределенного или пустого объекта. - Действия должны возвращать
payload
(стандартное соглашение об именах).
Рабочий пример: https://codesandbox.io/s/qqlyqwnm3j
Containers / ProductForm.js
import React, { Component } from "react";
import { connect } from "react-redux";
import { submitDetails } from "../actions/";
import { browserHistory } from "react-router";
class ProductForm extends Component {
state = {
name: "",
price: "",
manufacturer: "",
condition: "",
manufactureDate: "",
expirationDate: ""
};
handleChange = e => this.setState({ [e.target.name]: e.target.value });
handleSubmit = e => {
e.preventDefault();
const {
name,
price,
manufacturer,
condition,
manufactureDate,
expirationDate
} = this.state;
if (
!name ||
!price ||
!manufacturer ||
!condition ||
!manufactureDate ||
!expirationDate
)
return;
this.props.submitDetails({ ...this.state });
browserHistory.push("/details");
};
render = () => (
<div style={{ textAlign: "center" }} className="container">
<form
style={{ width: 400, margin: "0 auto" }}
onSubmit={this.handleSubmit}
>
<h1 style={{ textAlign: "center" }}>Enter Product Details</h1>
<div
style={{ textAlign: "left", marginBottom: 20 }}
className="form-group"
>
<label style={{ paddingLeft: 10 }}>Name</label>
<input
type="text"
className="uk-input"
name="name"
placeholder="Name of product"
onChange={this.handleChange}
value={this.state.name}
/>
</div>
<div
style={{ textAlign: "left", marginBottom: 20 }}
className="form-group"
>
<label style={{ paddingLeft: 10 }}>Price</label>
<input
type="number"
className="uk-input"
name="price"
placeholder="Product price"
onChange={this.handleChange}
value={this.state.price}
/>
</div>
<div
style={{ textAlign: "left", marginBottom: 20 }}
className="form-group"
>
<label style={{ paddingLeft: 10 }}>Manufacturer</label>
<input
type="text"
className="uk-input"
name="manufacturer"
placeholder="Product manufacturer"
onChange={this.handleChange}
value={this.state.manufacturer}
/>
</div>
<div
style={{ textAlign: "left", marginBottom: 20 }}
className="form-group"
>
<label style={{ paddingLeft: 10 }}>Condition</label>
<select
name="condition"
className="uk-select"
value={this.state.condition}
onChange={this.handleChange}
>
<option>Choose...</option>
<option>New</option>
<option>Used</option>
</select>
</div>
<div
style={{ textAlign: "left", marginBottom: 20 }}
className="form-group "
>
<label style={{ paddingLeft: 10 }}>Manufacture Date</label>
<input
type="date"
className="uk-input"
name="manufactureDate"
onChange={this.handleChange}
value={this.state.manufactureDate}
/>
</div>
<div
style={{ textAlign: "left", marginBottom: 20 }}
className="form-group "
>
<label style={{ paddingLeft: 10 }}>Expiration Date</label>
<input
type="date"
className="uk-input"
name="expirationDate"
onChange={this.handleChange}
value={this.state.text}
/>
</div>
<button type="submit" className="uk-button uk-button-primary">
Submit
</button>
</form>
</div>
);
}
export default connect(
null,
{ submitDetails }
)(ProductForm);
Containers / ShowDetails.js
import map from "lodash/map";
import isEmpty from "lodash/isEmpty";
import React from "react";
import { connect } from "react-redux";
const ShowDetails = ({ details }) =>
isEmpty(details) ? (
<div style={{ textAlign: "center", marginTop: 20 }}>
<h3 style={{ color: "red" }}>No Products Found!</h3>
</div>
) : (
<div style={{ textAlign: "center" }}>
<h1>Product Details </h1>
<table style={{ marginBottom: 10 }} className="products">
<thead className="thead-light">
<tr>
<th scope="col">Name</th>
<th scope="col">Price</th>
<th scope="col">Manufacturer</th>
<th scope="col">Condition</th>
<th scope="col">Manufacture Date</th>
<th scope="col">Expiration Date</th>
</tr>
</thead>
<tbody>
{map(
details,
(
{
name,
price,
manufacturer,
condition,
manufactureDate,
expirationDate
},
key
) => (
<tr key={key}>
<td>{name}</td>
<td>${price}</td>
<td>{manufacturer}</td>
<td>{condition}</td>
<td>{manufactureDate}</td>
<td>{expirationDate}</td>
</tr>
)
)}
</tbody>
</table>
</div>
);
export default connect(state => ({ details: state.product.details }))(
ShowDetails
);
Редукторы / index.js
import { routerReducer as routing } from "react-router-redux";
import { combineReducers } from "redux";
import { ADD_PRODUCT } from "../types";
const productReducer = (state = { details: [] }, { type, payload }) => {
switch (type) {
case ADD_PRODUCT:
return {
...state,
details: [...state.details, payload]
};
default:
return state;
}
};
export default combineReducers({
product: productReducer,
routing
});
actions / index.js
import { ADD_PRODUCT } from "../types";
export const submitDetails = payload => ({
type: ADD_PRODUCT,
payload
});