When I fill one field, the letters are transferred to another, can be transferred at all in a text block of another component. Before that everything worked fine, just a moment something went wrong, do not understand that. Please help.gif with problem here
my component:
import React from 'react'
import axios from 'axios'
import {Row, Col, FormGroup, ControlLabel, FormControl, HelpBlock} from 'react-bootstrap'
import SelectizeSimple from '../../SelectizeSimple'
import {connect} from 'react-redux'
import {bindActionCreators} from 'redux'
import {
setUserSecureId,
setUserName,
setCompany,
setAddressFirstLine,
setAddressSecondLine,
setCity,
setState,
setZip,
setVat,
setEmail
} from 'store/user'
class PersonalInfo extends React.Component{
constructor(props){
super(props)
this.state = {
inputName:''
}
}
submitFormPersonalInfo = (event) => {
axios.post('', {
query: 'mutation UpdateUser($form:UpdateUserInput!) { update_user (input:$form) { id } }',
variables: {form: this.props.updateUser},
}).then(res => {
console.log(res)
})
}
changeUserName = (event) => {
this.props.actions.setUserName(event.target.value)
}
changeCompany = (event) => {
this.props.actions.setCompany(event.target.value)
}
changeAddressFirstLine = (event) => {
this.props.actions.setAddressFirstLine(event.target.value)
}
changeAddressSecondLine = (event) => {
this.props.actions.setAddressSecondLine(event.target.value)
}
changeCity = (event) => {
this.props.actions.setCity(event.target.value)
}
changeState = (event) => {
this.props.actions.setState(event.target.value)
}
changeZIP = (event) => {
this.props.actions.setZip(event.target.value)
}
changeVat = (event) => {
this.props.actions.setVat(event.target.value)
}
changeEmail = (event) => {
this.props.actions.setEmail(event.target.value)
}
render(){
return(
<div>
<div className="tab-wrap-inner-header">
<div className="title_primary title_primary__inner">Personal Info</div>
</div>
<div className="form-wrap form-wrap__large-right-gap">
<form action="" className="form-default">
<FormGroup className="form-group__indent-bot-big">
<ControlLabel>
E-mail
</ControlLabel>
<div className="form-edit-box">
<FormControl placeholder="email#gmail.com"
onChange={this.changeEmail}
value={this.props.updateUser.email}/>
<button className="btn-edit btn-edit__left-gap">
Edit
</button>
</div>
<HelpBlock>This address will appear on your License.</HelpBlock>
</FormGroup>
<FormGroup className="form-group__indent-bot-big">
<ControlLabel>
Password
</ControlLabel>
<div className="form-edit-box">
<FormControl type="password" placeholder="*********"></FormControl>
<button className="btn-edit btn-edit__left-gap">
Edit
</button>
</div>
</FormGroup>
<FormGroup className="form-group__indent-bot-big">
<ControlLabel>
Name
</ControlLabel>
<FormControl
onChange={this.changeUserName}
value={this.props.updateUser.name}/>
</FormGroup>
<FormGroup className="form-group__indent-bot-big">
<ControlLabel>
Company Name (optional)
</ControlLabel>
<FormControl
onChange={this.changeCompany}
value={this.props.updateUser.company}
/>
<HelpBlock className="flexbox align-center">
<div className="radio-box radio-box__square radio-box__nogap">
<FormControl type="checkbox" id="radioGroup11"></FormControl>
<ControlLabel htmlFor="radioGroup11" className="radio-box__pseudo-radio"></ControlLabel>
</div>
<label htmlFor="radioGroup11" className="label-default">Hide information about my country from my profile</label>
</HelpBlock>
</FormGroup>
<FormGroup className="form-group__indent-bot-big">
<ControlLabel>
Adress Line 1
</ControlLabel>
<FormControl
onChange={this.changeAddressFirstLine}
value={this.props.updateUser.address_first_line}
/>
</FormGroup>
<FormGroup className="form-group__indent-bot-big">
<ControlLabel>
Adress Line 2 (optional)
</ControlLabel>
<FormControl
onChange={this.changeAddressSecondLine}
value={this.props.updateUser.address_second_line}
/>
</FormGroup>
<FormGroup className="form-group__indent-bot-big">
<ControlLabel>
City
</ControlLabel>
<FormControl
onChange={this.changeCity}
value={this.props.updateUser.city}
/>
</FormGroup>
<FormGroup className="form-group__indent-bot-big">
<ControlLabel>
State / Province / Region
</ControlLabel>
<FormControl
onChange={this.changeState}
value={this.props.updateUser.state}
/>
</FormGroup>
<FormGroup className="form-group__indent-bot-big">
<ControlLabel>
ZIP / Postal Code
</ControlLabel>
<FormControl
onChange={this.changeZIP}
value={this.props.updateUser.zip}
/>
</FormGroup>
<FormGroup className="form-group__indent-bot-big">
<ControlLabel>
VAT number (if applicable)
</ControlLabel>
<FormControl
onChange={this.changeVat}
value={this.props.updateUser.vat}
/>
</FormGroup>
<button type="button" className="btn btn-primary btn-primary__submit" onClick={this.submitFormPersonalInfo}>
Save Changes
</button>
</form>
</div>
</div>
)
}
}
const mapStateToProps = (state) => {
return ({
updateUser: state.user.updateUser,
})
}
const mapDispatchToProps = (dispatch) => ({
actions: bindActionCreators({
setUserSecureId,
setUserName,
setCompany,
setAddressFirstLine,
setAddressSecondLine,
setCity,
setState,
setZip,
setVat,
setEmail
}, dispatch),
})
export default connect(mapStateToProps, mapDispatchToProps)(PersonalInfo)
my reducer:
import axios from 'axios'
export const GET_USER_INFO = 'GET_USER_INFO'
export const GET_CURRENT_USER = 'GET_CURRENT_USER'
export const SET_USER_AVATAR = 'SET_USER_AVATAR'
export const SET_USER_BANNER = 'SET_USER_BANNER'
export const SET_USER_SECURE_ID = 'SET_USER_SECURE_ID'
export const SET_USER_NAME = 'SET_USER_NAME'
export const SET_COMPANY = 'SET_COMPANY'
export const SET_ADDRESS_FIRST_LINE = 'SET_COMPANY'
export const SET_ADDRESS_SECOND_LINE = 'SET_COMPANY'
export const SET_CITY = 'SET_CITY'
export const SET_STATE = 'SET_STATE'
export const SET_ZIP = 'SET_ZIP'
export const SET_VAT = 'SET_VAT'
export const SET_BIOGRAPHY = 'SET_VAT'
export const ADD_TRACK_TO_FAVORITES = 'ADD_TRACK_TO_FAVORITES'
export const SET_SOCIAL_LINKS = 'SET_SOCIAL_LINKS'
export const SET_EMAIL = 'SET_EMAIL'
const initialState = {
userInfo: {},
currentUser: {},
updateUser: {
id: '',
name: "",
avatar: "",
address_first_line: "",
address_second_line: "",
city: "",
state: "",
zip: "",
vat: "",
company: "",
biography: "",
secure_id: "",
},
favorite_tracks:[],
social_links:[]
}
export const getUserInfo = (user) =>
(dispatch, getState) => {
var state = getState();
return axios.post(API_URL, {
query: "query User($id:ID) {user(id:$id) { id name email avatar social_links { type url } banner email biography address_first_line address_second_line city state zip vat company secure_id created_at is_current tracks { id name logo created_at composer publisher } favorite_tracks { id name logo created_at is_favorite user { id name } } followers { id name tracks_count } followings { id name tracks_count }}}",
variables: {id: user}
}).then(res => {
dispatch({
type: GET_USER_INFO,
data: res.data.data.user
})
})
}
export const getCurrentUser = () =>
(dispatch, getState) => {
var state = getState();
return axios.post(API_URL, {
query: "{ current_user () { id name email biography created_at is_current } }"
}).then(res => {
dispatch({
type: GET_CURRENT_USER,
data: res.data.data.current_user
})
})
}
export const AddTrackToFavorites = (id) =>
(dispatch, getState) => {
var state = getState();
return axios.post(API_URL, {
query: "mutation AddTrackToFavorites($input:AddTrackToFavoritesInput!) { add_track_to_favorites (input:$input) { id } }",
variables: { input: { id: id } }
}).then(res => {
})
}
export const RemoveTrackFromFavorites = (id) =>
(dispatch, getState) => {
var state = getState();
return axios.post(API_URL, {
query: "mutation RemoveTrackFromFavorites($input:RemoveTrackFromFavoritesInput!) {remove_track_from_favorites (input:$input) { id }}",
variables: { input: { id: id } }
}).then(res => {
})
}
export const setUserAvatar = (avatar) =>
(dispatch) => {
dispatch({
type: SET_USER_AVATAR,
data: avatar
})
}
export const setUserBanner = (banner) =>
(dispatch) => {
dispatch({
type: SET_USER_BANNER,
data: banner
})
}
export const setUserName = (name) =>
(dispatch) => {
dispatch({
type: SET_USER_NAME,
data: name
})
}
export const setCompany = (company) =>
(dispatch) => {
dispatch({
type: SET_COMPANY,
data: company
})
}
export const setAddressFirstLine = (address) =>
(dispatch) => {
dispatch({
type: SET_ADDRESS_FIRST_LINE,
data: address
})
}
export const setAddressSecondLine = (address) =>
(dispatch) => {
dispatch({
type: SET_ADDRESS_SECOND_LINE,
data: address
})
}
export const setCity = (city) =>
(dispatch) => {
dispatch({
type: SET_CITY,
data: city
})
}
export const setState = (state) =>
(dispatch) => {
dispatch({
type: SET_STATE,
data: state
})
}
export const setEmail = (state) =>
(dispatch) => {
dispatch({
type: SET_EMAIL,
data: state
})
}
export const setZip = (zip) =>
(dispatch) => {
dispatch({
type: SET_ZIP,
data: zip
})
}
export const setVat = (vat) =>
(dispatch) => {
dispatch({
type: SET_VAT,
data: vat
})
}
export const setBiography = (biography) =>
(dispatch) => {
dispatch({
type: SET_BIOGRAPHY,
data: biography
})
}
export const setSocialLinks = (links) =>
(dispatch) => {
dispatch({
type: SET_SOCIAL_LINKS,
data: links
})
}
export const setUserSecureId = (value) =>
(dispatch) => {
dispatch({
type: SET_USER_SECURE_ID,
data: value,
})
}
const ACTION_HANDLERS = {
[GET_USER_INFO]: (state, action) => {
return ({
...state,
userInfo: {
...state.userInfo,
created_at: action.data.created_at,
is_current: action.data.is_current,
tracks: action.data.tracks,
followers: action.data.followers,
followings: action.data.followings,
favorite_tracks: action.data.favorite_tracks,
},
updateUser: {
...state.updateUser,
id: action.data.id,
email: action.data.email,
secure_id: action.data.secure_id,
name: action.data.name,
avatar: action.data.avatar,
banner: action.data.banner,
company: action.data.company,
address_first_line: action.data.address_first_line,
address_second_line: action.data.address_second_line,
city: action.data.city,
state: action.data.state,
zip: action.data.zip,
vat: action.data.vat,
biography: action.data.biography,
social_links: action.data.social_links
},
favorite_tracks: action.data.favorite_tracks,
social_links: action.data.social_links
})
},
[GET_CURRENT_USER]: (state, action) => {
return ({
...state,
currentUser: action.data
})
},
[SET_USER_AVATAR]: (state, action) => {
return ({
...state,
updateUser: {
...state.updateUser,
avatar: action.data
}
})
},
[SET_USER_BANNER]: (state, action) => {
return ({
...state,
updateUser: {
...state.updateUser,
banner: action.data
}
})
},
[SET_USER_NAME]: (state, action) => {
return ({
...state,
updateUser: {
...state.updateUser,
name: action.data
}
})
},
[SET_COMPANY]: (state, action) => {
return ({
...state,
updateUser: {
...state.updateUser,
company: action.data
}
})
},
[SET_ADDRESS_FIRST_LINE]: (state, action) => {
return ({
...state,
updateUser: {
...state.updateUser,
address_first_line: action.data
}
})
},
[SET_ADDRESS_SECOND_LINE]: (state, action) => {
return ({
...state,
updateUser: {
...state.updateUser,
address_second_line: action.data
}
})
},
[SET_CITY]: (state, action) => {
return ({
...state,
updateUser: {
...state.updateUser,
city: action.data
}
})
},
[SET_STATE]: (state, action) => {
return ({
...state,
updateUser: {
...state.updateUser,
state: action.data
}
})
},
[SET_ZIP]: (state, action) => {
return ({
...state,
updateUser: {
...state.updateUser,
zip: action.data
}
})
},
[SET_VAT]: (state, action) => {
return ({
...state,
updateUser: {
...state.updateUser,
vat: action.data
}
})
},
[SET_BIOGRAPHY]: (state, action) => {
return ({
...state,
updateUser: {
...state.updateUser,
biography: action.data
}
})
},
[SET_USER_SECURE_ID]: (state, action) => {
return ({
...state,
updateUser: {
...state.updateUser,
secure_id: action.data
}
})
},
[SET_EMAIL]: (state, action) => {
return ({
...state,
updateUser: {
...state.updateUser,
email: action.data
}
})
},
[ADD_TRACK_TO_FAVORITES]: (state, action) => {
return ({
...state,
favorite_tracks: action.data
})
},
[SET_SOCIAL_LINKS]: (state, action) => {
return ({
...state,
social_links: action.data
})
},
}
export default function userReducer(state = initialState, action) {
const handler = ACTION_HANDLERS[action.type]
return handler ? handler(state, action) : state
}
Your problem is because same constants for multiple redux action ids:
export const SET_COMPANY = 'SET_COMPANY'
export const SET_ADDRESS_FIRST_LINE = 'SET_COMPANY'
export const SET_ADDRESS_SECOND_LINE = 'SET_COMPANY'
need replace with:
export const SET_COMPANY = 'SET_COMPANY'
export const SET_ADDRESS_FIRST_LINE = 'SET_ADDRESS_FIRST_LINE'
export const SET_ADDRESS_SECOND_LINE = 'SET_ADDRESS_SECOND_LINE'
Related
there are similiar questions in stackoverflow but I I did not find what I was looking for.
I have a donorDonationForm which is a class componenet that connected to the redux state. The porpuse of that componenet is to collect inormation about a person that want to donate electronics items. At this point, I want to save those items in an array (maybe with an object in the future).
my redux state save the donor info and the reducer looks like this:
import {CHANGE_INPUT_FIELD} from '../utils/constants';
const initialStateInputs = {
// update the state
donorFields: {
name: '',
phone: '',
area: '',
yeshuv: '',
address: ''
// dateOfOffer: ''
},
donationFields: {
// donorID: '',
// vulonteerID: '',
type: [],
quantity: 1,
status: 'NOT_HANDLED',
comments: ''
// lastDateHandled: ''
}
// }, items: [ //need to add quantity
// {id: 1, name: "LAPTOP", isChecked: false, label: 'מחשב'},
// {id: 2, name: "HEADPHONES", isChecked: false, label: 'אוזניות'},
// {id: 3, name: "OTHER", isChecked: false, label: 'אחר'},
// ]
}
export const donorDonationInputsReducer = ( state = initialStateInputs, action={} ) => {
switch(action.type) {
case CHANGE_INPUT_FIELD:
return Object.assign( {}, state,
{
donorFields : {...state.donorFields,...action.payload},
donationFields: {...state.donationFields,...action.payload},
// items : {...state.items,...action.payload},
// isChecked: action.payload
})
default:
return state;
}
}
As you can see the items is commented by now, and I am managing the state of the item in a local state, and that how the comp looks like:
import React, {Component} from 'react';
import { connect } from 'react-redux';
import { setInputField } from '../actions/formAction';
import CheckBox from '../components/CheckBox/CheckBox';
import FormInput from '../components/FormInput/FormInput';
import {selectAreasOptions_2} from '../utils/constants';
import "./form.css";
const mapStateToProps = (state) => {
return {
donorFields: state.donorDonationInputsReducer.donorFields,
donationFields: state.donorDonationInputsReducer.donationFields
}
}
const mapDispatchToProps = dispatch => {
return {
onInputChange: event => {
const {name, value} = event.target;
dispatch(setInputField( { [name]:value} ) )
}
}
}
class donorDonationForm extends Component {
constructor() {
super();
this.state = {
items: [
{id: 1, name: "LAPTOP", isChecked: false, label: 'מחשב'},
{id: 2, name: "HEADPHONES", isChecked: false, label: 'אוזניות'},
{id: 3, name: "OTHER", isChecked: false, label: 'אחר'},
]
,
type: []
}
}
handleCheckChieldElement = (event) => {
let {items, type} = this.state;
let arr = [];
items.forEach(item => {
if (item.name === event.target.value) {
item.isChecked = event.target.checked;
// console.log(`item.name :${item.name }`);
// console.log(`event.target.value :${event.target.value}`);
// console.log(`event.target.checked :${event.target.checked}`);
}
})
items.map(item => item.isChecked ? arr.push(item.name) : null)
this.setState({items: [...items], type: [...arr]});
}
onButtonSubmit = (event) => {
console.log(this.props.donorFields);
event.preventDefault();
fetch('http://localhost:8000/api/donor', {
method: 'post',
headers: {
'Accept': 'application/json',
'Content-Type': 'application/json'
},
body: JSON.stringify({
...this.props.donorFields
})
})
.then(response => response.json())
.then(resp => console.log(resp))
.catch( err => console.log(err) )
}
// componentDidUpdate(prevProps, prevState) {
// const {items, type} = this.state;
// // const type = [];
// if (prevState.items !== items) {
// console.log('items state has changed');
// items.map (item => item.isChecked ?
// this.setState({type: [...type,item.name]}) : null)
// // if (item.isChecked) { type.push(item.name) } ;
// console.log(type);
// }
// }
render() {
console.log(this.state.items);
console.log(this.state.type);
const { onInputChange } = this.props;
return (
<div>
<h1 className="pt4"> פרטי תורם</h1>
<form className=" black-80 pt2" >
<section className=" grid-container">
<FormInput
id="name"
name="name"
type="text"
onInputChange={onInputChange}
label="שם "
required
/>
<FormInput
id="phone"
name="phone"
type="tel"
onInputChange={onInputChange}
label="מספר טלפון "
required
/>
<FormInput
id="address"
name="address"
type="text"
onInputChange={onInputChange}
label="כתובת "
required
/>
<FormInput
id="yeshuv"
name="yeshuv"
type="text"
onInputChange={onInputChange}
label="עיר "
required
/>
<FormInput
id="comments"
name="comments"
onInputChange={onInputChange}
label="הערות "
required
/>
<FormInput
id="area"
name="area"
onInputChange={onInputChange}
label="איזור "
select={selectAreasOptions_2}
/>
{/* type */}
<div className="measure-narrow">
<label htmlFor="type" className="f5 b db mb2">מעוניין לתרום
<span className="normal black-60"> *</span>
</label>
{
this.state.items.map( (item, i) => {
return (
<CheckBox
key={i}
onChange={this.handleCheckChieldElement}
checked={ item.isChecked }
value= {item.name}
label = {item.label}
/>
);
})
}
</div>
</section>
<input type="submit" value="שלח"
className="b bg-light-blue pa2 hover pointer"
onClick={this.onButtonSubmit}
/>
</form>
</div>
);
}
}
export default connect(mapStateToProps, mapDispatchToProps)(donorDonationForm);
My main goal is that the type array - the final donation, will update the redux state before submitting this form. I tried with componentDidUpdate but didn't make it. What is the best way for tracking the checked items, updating the array and then update the type array which is the final donation in the redux state? should I do that in the onButtonSubmit method - before sending the data to the server (and thats way saving the looping over the items array for searching the checked elements) ?
Better approach would be do inside onButtonSubmit
Let me briefly explain the tasks:
inputChangeHandler to update this.state.items
Go with the final this.state.items value Array of items inside onButtonSubmit
After getting API response update the application level Redux state with Array of items.
Note: Dispatch the action. Reducer will update the Redux state. Following code will do this:
// Action
export const setItems = (data) => (dispatch) => {
dispatch({type: 'SET_ITEMS', payload: data})
}
// mapDispatchToProps
const mapDispatchToProps = (dispatch) =>
bindActionCreators(
{
setItems,
...others
},
dispatch
)
// onSubmitButton
onButtonSubmit = (event) => {
console.log(this.props.donorFields);
event.preventDefault();
fetch('http://localhost:8000/api/donor', {
method: 'post',
headers: {
'Accept': 'application/json',
'Content-Type': 'application/json'
},
body: JSON.stringify({
...this.props.donorFields
})
})
.then(response => this.props.setItems(response.json())) // will update the state.
.then(resp => console.log(resp))
.catch( err => console.log(err) )
}
// Reducer
export const donorDonationInputsReducer = ( state = initialStateInputs, action={} ) => {
switch(action.type) {
case CHANGE_INPUT_FIELD:
return Object.assign( {}, state,
{
donorFields : {...state.donorFields,...action.payload},
donationFields: {...state.donationFields,...action.payload},
// items : {...state.items,...action.payload},
// isChecked: action.payload
})
case SET_ITEMS:
return {
...state,
items: action.payload
}
default:
return state;
}
}
That's it.
Happy Coding :)
I am working with ReactJs and Fetch API from Swagger UI. I code for Room management, this is my code for that
In actions.js file
// Room type action
export const actFetchRoomTypesRequest = () => {
return (dispatch) => {
return callApi('admin/host-room-types', 'GET', null).then((res) => {
dispatch(actFetchRoomTypes(res.data));
});
};
};
export const actFetchRoomTypes = (roomTypes) => {
return {
type: Types.FETCH_ROOMTYPES,
roomTypes,
};
};
export const actDeleteRoomTypeRequest = (id) => {
return (dispatch) => {
return callApi(`admin/host-room-types/${id}`, 'DELETE', null).then((res) => {
dispatch(actFetchRoomTypesRequest());
});
};
};
export const actAddRoomTypeRequest = (roomType) => {
return (dispatch) => {
return callApi(`admin/host-room-types`, 'POST', roomType).then((res) => {
return callApi(`admin/host-room-types`).then((ress) => {
dispatch(actFetchRoomTypes(ress.data));
});
});
};
};
export const actAddRoomType = (roomType) => {
return {
type: Types.ADD_ROOMTYPE,
roomType,
};
};
export const actGetRoomTypeRequest = (id) => {
return (dispatch) => {
return callApi(`admin/host-room-types/${id}`, 'GET', null).then((res) => {
dispatch(actGetRoomType(res.data));
});
};
};
export const actGetRoomType = (roomType) => {
return {
type: Types.EDIT_ROOMTYPE,
roomType,
};
};
export const actUpdateRoomTypeRequest = (roomType) => {
return (dispatch) => {
return callApi(`admin/host-room-types/${roomType.id}`, 'PUT', roomType).then((res) => {
return callApi(`admin/host-room-types`).then((ress) => {
dispatch(actFetchRoomTypes(ress.data));
});
});
};
};
export const actUpdateRoomType = (roomType) => {
return {
type: Types.UPDATE_ROOMTYPE,
roomType,
};
};
In RoomTypeItem.js which the place I show all action for that
import React, { Component } from 'react';
import { Link } from 'react-router-dom';
class RoomTypeItem extends Component {
constructor(props) {
super(props);
console.log('constructor');
}
onDelete = (id) => {
console.log(id);
if (window.confirm('ban muon xoa?')) {
this.props.onDelete(id);
}
};
onSelectRoomType = (id) => {
console.log('onSelect' + id);
this.props.onSelect(id);
};
render() {
var { roomType, index } = this.props;
console.log('render' + roomType.id + this.props.checked);
return (
<tr>
<td>
<input
type="checkbox"
className="delid[]"
checked={this.props.checked}
onChange={() => this.onSelectRoomType(roomType.id)}
/>
</td>
<td>{index + 1}</td>
<td>{roomType.name}</td>
<td>{roomType.description}</td>
<td>
<Link to={`/roomType/${roomType.id}/edit`} className="btn btn-warning mr-10">
Update
</Link>
</td>
</tr>
);
}
}
export default RoomTypeItem;
And in RoomTypeActionPage have code like this
import React, { Component } from 'react';
import { Link } from 'react-router-dom';
import { actAddRoomTypeRequest, actGetRoomTypeRequest, actUpdateRoomTypeRequest } from './../../actions/index';
import { connect } from 'react-redux';
class RoomTypeActionPage extends Component {
constructor(props) {
super(props);
this.state = {
id: '',
txtName: '',
txtDescription: '',
};
}
componentDidMount() {
var { match } = this.props;
if (match) {
var id = match.params.id;
this.props.onEditRoomType(id);
}
}
componentWillReceiveProps(nextProps) {
if (nextProps && nextProps.itemEditing) {
var { itemEditing } = nextProps;
this.setState({
id: itemEditing.id,
txtName: itemEditing.name,
txtDescription: itemEditing.description,
});
}
}
onChange = (e) => {
var target = e.target;
var name = target.name;
var value = target.type === 'checkbox' ? target.checked : target.value;
this.setState({
[name]: value,
});
};
onSave = (e) => {
e.preventDefault();
var { id, txtName, txtDescription } = this.state;
var { history } = this.props;
var roomType = {
id: id,
name: txtName,
description: txtDescription,
};
if (id) {
//update
this.props.onUpdateRoomType(roomType);
} else {
this.props.onAddRoomType(roomType);
}
history.goBack();
};
render() {
var { txtName, txtDescription } = this.state;
return (
<div className="col-xs-6 col-sm-6 col-md-6 col-lg-6">
<form onSubmit={this.onSave}>
<div className="form-group">
<label>Name:</label>
<input
type="text"
className="form-control"
name="txtName"
value={txtName}
onChange={this.onChange}
/>
</div>
<div className="form-group">
<label>Description:</label>
<input
type="text"
className="form-control"
name="txtDescription"
value={txtDescription}
onChange={this.onChange}
/>
</div>
<Link to="/roomType-list" className="btn btn-danger mr-10">
Cancel
</Link>
<button type="submit" className="btn btn-primary">
Save
</button>
</form>
</div>
);
}
}
const mapStateToProps = (state) => {
return {
itemEditing: state.itemEditing,
};
};
const mapDispatchToProps = (dispatch, props) => {
return {
onAddRoomType: (roomType) => {
dispatch(actAddRoomTypeRequest(roomType));
},
onEditRoomType: (id) => {
dispatch(actGetRoomTypeRequest(id));
},
onUpdateRoomType: (roomType) => {
dispatch(actUpdateRoomTypeRequest(roomType));
},
};
};
export default connect(mapStateToProps, mapDispatchToProps)(RoomTypeActionPage);
And this is my Reducer
import * as Types from './../constants/ActionTypes';
var initialState = {
loading: false,
data: [],
};
var findIndex = (roomTypes, id) => {
var result = -1;
roomTypes.data.forEach((roomType, index) => {
if (roomType.id === id) {
result = index;
}
});
return result;
};
const roomTypes = (state = initialState, action) => {
var index = -1;
var { id, roomType } = action;
switch (action.type) {
case Types.FETCH_ROOMTYPES:
state = action.roomTypes;
return { ...state };
case Types.DELETE_ROOMTYPE:
index = findIndex(state, id);
state.splice(index, 1);
return { ...state };
case Types.ADD_ROOMTYPE:
state.data.push(action.roomType);
return { ...state };
case Types.UPDATE_ROOMTYPE:
index = findIndex(state, roomType.id);
state[index] = roomType;
return { ...state };
default:
return { ...state };
}
};
export default roomTypes;
I do not know when I run my code it shows me an error like this
I already to check all but I can realize where I code wrong for that. PLease help me, I need you, Thank thank so much
This is my link for swagger UI : here
This is my apiCaller
import axios from 'axios';
import * as Config from './../constants/Config';
export default function callApi(endpoint, method = 'GET', body) {
return axios({
method: method,
url: `${Config.API_URL}/${endpoint}`,
data: body,
headers: {
Accept: 'application/json',
'Content-Type': 'application/json',
Authorization:
'Bearer ' +
'eyJhbGciOiJIUzUxMiJ9.eyJzdWIiOiJhZG1pbiIsImlhdCI6MTU5Njg4MTYxMSwiZXhwIjoxNjEyOTQzNjEwfQ.eXkXWuuIMk94uOtVZQSNysbfJyIQuP5dIFqS06Kx-KsYVCkVEAbU01IhwJpkR4YtgXt8idkN4MM0cT76Xre7Sg',
},
}).catch((err) => {
console.log(err);
});
}
I am building a social media application using the MERN stack and Redux as the state manager. I have a feed component which renders PostItem components which display the post and allow for actions such as liking, and commenting. I also have a Post component that renders the same PostItem component that opens when the user clicks the comment button on the PostItem component in the feed. When I like a post via the feed component it receives the updated props and rerenders the component showing the changes. However when I click the like button in the Post component it updates the Redux store but does not receive the updated props.
Feed.js
import React, { Fragment, useEffect } from 'react';
import PropTypes from 'prop-types';
import { connect } from 'react-redux';
import { getPosts } from '../../actions/post';
// Components
import UserProfileCard from './UserProfileCard';
import PostForm from './PostForm';
import Footer from '../layout/Footer';
import PostItem from '../posts/PostItem';
import Spinner from '../layout/Spinner';
const Feed = ({ getPosts, post: { posts, loading } }) => {
//Same as component did mount
useEffect(() => {
getPosts();
}, [getPosts]);
return (
<Fragment>
<div className='main-container mt-3'>
<div className='container'>
<div className='row'>
<UserProfileCard />
<PostForm />
</div>
{loading ? (
<Spinner />
) : (
posts.map(post => <PostItem key={post._id} post={post} />)
)}
</div>
</div>
<Footer />
</Fragment>
);
};
Feed.propTypes = {
getPosts: PropTypes.func.isRequired,
post: PropTypes.object.isRequired
};
const mapStateToProps = state => ({
post: state.post
});
export default connect(mapStateToProps, { getPosts })(Feed);
Post.js
import React, { useEffect } from 'react';
import PropTypes from 'prop-types';
import { connect } from 'react-redux';
import { getPost } from '../../actions/post';
import { Link } from 'react-router-dom';
// Components
import Spinner from '../layout/Spinner';
import PostItem from './PostItem';
import PostCommentForm from './PostCommentForm';
import Comment from './Comment';
// Assets
import { ArrowLeft } from 'react-feather';
const Post = ({ getPost, post: { post, loading }, match }) => {
useEffect(() => {
// get id from url in params for getPost function
getPost(match.params.id);
}, [getPost]);
return loading || post === null ? (
<Spinner />
) : (
<div className='main-container mt-3'>
<div className='container'>
<div className='row'>
{/* TODO ADD BROWSER HISTORY FUNCTIONALITY TO ALLOW USER TO GO BACK TO PROFILE OR FEED */}
<Link className='mb-1' to='/feed'>
<button className='btn btn-logo-color'>
<ArrowLeft />
</button>
</Link>
<PostItem key={post._id} post={post} />
<PostCommentForm />
</div>
<Comment />
</div>
</div>
);
};
Post.propTypes = {
getPost: PropTypes.func.isRequired,
post: PropTypes.object.isRequired
};
const mapStateToProps = state => ({
post: state.post
});
export default connect(mapStateToProps, { getPost })(Post);
PostItem.js
import React from 'react';
import PropTypes from 'prop-types';
import { Link } from 'react-router-dom';
import Moment from 'react-moment';
import { connect } from 'react-redux';
import { addLike, removeLike, deletePost } from '../../actions/post';
// Assets
import { ThumbsUp, ThumbsDown, MessageSquare, XCircle } from 'react-feather';
import avi from '../assets/default-avatar.png';
const PostItem = ({
auth,
post: { _id, text, firstname, lastname, user, likes, comments, date },
addLike,
removeLike,
deletePost
}) => {
return (
<div className='card mt-1 post'>
<div className='card-body'>
<div className='row'>
<div className='col-lg-2'>
<Link className='feed-link' to={`/profile/${user}`}>
<img src={avi} alt='avatar' className='avatar' />
<h5 className='card-title mt-2'>
{firstname} {lastname}
</h5>
</Link>
</div>
<div className='col-lg-10'>
<p className='card-text'>{text}</p>
<p className='text-muted post-date'>
<Moment format='LLL'>{date}</Moment>
</p>
<div className='post-buttons'>
<button
type='button'
className='btn btn-outline-primary mr-1'
onClick={e => addLike(_id)}
>
<ThumbsUp />
<span className='badge badge-light'>
{likes.length > 0 && <span>{likes.length}</span>}
</span>
</button>
<button
type='button'
className='btn btn-outline-danger mr-1'
onClick={e => removeLike(_id)}
>
<ThumbsDown />
</button>
{/* TODO ADD CONDITIONAL RENDERING TO REMOVE WHEN POST IS OPEN */}
<Link to={`/post/${_id}`}>
<button type='button' className='btn btn-outline-info mr-1'>
<MessageSquare />
<span className='badge badge-light'>
{comments.length > 0 && (
<span className='comment-count'>{comments.length}</span>
)}
</span>
</button>
</Link>
{!auth.loading && user === auth.user._id && (
<button
type='button'
className='btn btn-outline-danger mr-1'
onClick={() => deletePost(_id)}
>
<XCircle />
</button>
)}
</div>
</div>
</div>
</div>
</div>
);
};
PostItem.propTypes = {
post: PropTypes.object.isRequired,
auth: PropTypes.object.isRequired,
addLike: PropTypes.func.isRequired,
removeLike: PropTypes.func.isRequired,
deletePost: PropTypes.func.isRequired
};
const mapStateToProps = state => ({
auth: state.auth
});
export default connect(mapStateToProps, { addLike, removeLike, deletePost })(
PostItem
);
Post Reducer
import {
GET_POSTS,
POST_ERROR,
UPDATE_LIKES,
DELETE_POST,
ADD_POST,
GET_POST,
ADD_COMMENT,
REMOVE_COMMENT
} from '../actions/types';
const initialState = {
posts: [],
post: null,
loading: true,
error: {}
};
export default function(state = initialState, action) {
const { type, payload } = action;
switch (type) {
case GET_POSTS:
return {
...state,
posts: payload,
loading: false
};
case GET_POST:
return {
...state,
post: payload,
loading: false
};
case ADD_POST:
return {
...state,
posts: [payload, ...state.posts],
loading: false
};
case DELETE_POST:
return {
...state,
posts: state.posts.filter(post => post._id !== payload),
loading: false
};
case POST_ERROR:
return {
...state,
error: payload,
loading: false
};
case UPDATE_LIKES:
return {
...state,
posts: state.posts.map(post =>
post._id === payload.postId ? { ...post, likes: payload.likes } : post
),
loading: false
};
case ADD_COMMENT:
return {
...state,
post: { ...state.post, comments: payload },
loading: false
};
case REMOVE_COMMENT:
return {
...state,
post: {
...state.post,
comments: state.post.comments.filter(
comment => comment._id !== payload
)
},
loading: false
};
default:
return {
...state
};
}
}
Post actions
import axios from 'axios';
import { setAlert } from './alert';
import {
GET_POSTS,
POST_ERROR,
UPDATE_LIKES,
DELETE_POST,
ADD_POST,
GET_POST,
ADD_COMMENT,
REMOVE_COMMENT
} from './types';
//Get posts
export const getPosts = () => async dispatch => {
try {
const res = await axios.get('/api/posts');
dispatch({
type: GET_POSTS,
payload: res.data
});
} catch (error) {
dispatch({
type: POST_ERROR,
payload: { msg: error.response.data.msg, status: error.response.status }
});
}
};
// Add like
export const addLike = postId => async dispatch => {
try {
const res = await axios.put(`/api/posts/like/${postId}`);
dispatch({
type: UPDATE_LIKES,
payload: { postId, likes: res.data }
});
} catch (error) {
dispatch({
type: POST_ERROR,
payload: { msg: error.response.data.msg, status: error.response.status }
});
}
};
// Remove like
export const removeLike = postId => async dispatch => {
try {
const res = await axios.put(`/api/posts/unlike/${postId}`);
dispatch({
type: UPDATE_LIKES,
payload: { postId, likes: res.data }
});
} catch (error) {
dispatch({
type: POST_ERROR,
payload: { msg: error.response.data.msg, status: error.response.status }
});
}
};
// Add Post
export const addPost = formData => async dispatch => {
const config = {
headers: {
'Content-Type': 'application/json'
}
};
try {
const res = await axios.post('/api/posts', formData, config);
dispatch({
type: ADD_POST,
payload: res.data
});
dispatch(setAlert('Post Created', 'success'));
} catch (error) {
dispatch({
type: POST_ERROR,
payload: { msg: error.response.data.msg, status: error.response.status }
});
}
};
// Delete Post
export const deletePost = id => async dispatch => {
try {
const res = await axios.delete(`/api/posts/${id}`);
dispatch({
type: DELETE_POST,
payload: id
});
dispatch(setAlert('Post Removed', 'success'));
} catch (error) {
dispatch({
type: POST_ERROR,
payload: { msg: error.response.data.msg, status: error.response.status }
});
}
};
//Get post
export const getPost = id => async dispatch => {
try {
const res = await axios.get(`/api/posts/${id}`);
dispatch({
type: GET_POST,
payload: res.data
});
} catch (error) {
dispatch({
type: POST_ERROR,
payload: { msg: error.response.data.msg, status: error.response.status }
});
}
};
// Add Comment
export const addComment = (postId, formData) => async dispatch => {
const config = {
headers: {
'Content-Type': 'application/json'
}
};
try {
const res = await axios.post(
`/api/posts/comment/${postId}`,
formData,
config
);
dispatch({
type: ADD_COMMENT,
payload: res.data
});
dispatch(setAlert('Comment Added', 'success'));
} catch (error) {
dispatch({
type: POST_ERROR,
payload: { msg: error.response.data.msg, status: error.response.status }
});
}
};
// Delete Comment
export const deleteComment = (postId, commentId) => async dispatch => {
try {
const res = await axios.delete(`/api/posts/comment/${postId}/${commentId}`);
dispatch({
type: REMOVE_COMMENT,
payload: commentId
});
dispatch(setAlert('Comment Removed', 'success'));
} catch (error) {
dispatch({
type: POST_ERROR,
payload: { msg: error.response.data.msg, status: error.response.status }
});
}
};
You're updating the posts array here which is what you use to render the PostItems in Feed.
case UPDATE_LIKES:
return {
...state,
posts: state.posts.map(post =>
post._id === payload.postId ? { ...post, likes: payload.likes } : post
),
loading: false
};
However in Post.js you use the Post object, not the Posts array. Post has not been updated by the UPDATE_LIKES action so your component doesn't re-render.
I have a problem when want to show
const mapStateToProps = state => {
return {
loading: state.auth.loading,
error: state.auth.error,
userId: state.auth.userId,
tokenId: state.auth.token
}
}
this in my function
register = (event) => {
event.preventDefault()
this.props.onAuth( this.state.email, this.state.password, this.state.isSignup );
localStorage.setItem('token', this.props.tokenId);
localStorage.setItem('userId', this.props.userId);
}
I see token and userId after the second click. But I can't see after the first click. What I need more to show immediately?
This is my auth.js reducers
import * as actionTypes from '../actions/actionsTypes';
import { updateObject } from '../utility';
const initialState = {
token: null,
userId: null,
error: null,
loading: false
};
const authStart = ( state, action ) => {
return updateObject( state, { error: null, loading: true } );
};
const authSuccess = (state, action) => {
return updateObject( state, {
token: action.idToken,
userId: action.userId,
error: null,
loading: false
} );
};
const authFail = (state, action) => {
return updateObject( state, {
error: action.error,
loading: false
});
}
const reducer = ( state = initialState, action ) => {
switch ( action.type ) {
case actionTypes.AUTH_START: return authStart(state, action);
case actionTypes.AUTH_SUCCESS: return authSuccess(state, action);
case actionTypes.AUTH_FAIL: return authFail(state, action);
default:
return state;
}
};
export default reducer;
But, after the first click, I got token in my render function.
{this.props.tokenId}
Could you please help me? I think I need to use async/await. But I am not sure.
Here you go Header.js
import React, { Component } from 'react'
import { connect } from 'react-redux'
import * as actions from '../../store/actions/index'
import PropTypes from 'prop-types'
import './header.css'
class Header extends Component {
constructor(props) {
super(props)
this.state = {
email: '',
password: '',
isSignup: true,
token: false
}
this.handleChange = this.handleChange.bind(this);
}
handleChange (evt) {
this.setState({ [evt.target.name]: evt.target.value });
}
switchAuthModeHandler = (event) => {
event.preventDefault()
this.setState(prevState => {
return {
isSignup: !prevState.isSignup
}
})
}
register = (event) => {
event.preventDefault()
this.props.onAuth( this.state.email, this.state.password, this.state.isSignup );
localStorage.setItem('token', this.props.tokenId);
localStorage.setItem('userId', this.props.userId);
}
render() {
let regBtn = ''
if (this.state.isSignup) {
regBtn = 'Register'
}
else {
regBtn = 'Login'
}
let login = null
if(!this.props.tokenId) {
login = (
<div className="login">
<form onSubmit={this.register}>
<input type="email" placeholder="email" name="email" onChange={this.handleChange} />
<input type="password" placeholder="password" name="password" onChange={this.handleChange} />
<button>{regBtn}</button>
</form>
<div onClick={this.switchAuthModeHandler} className="switch">Switch to {this.state.isSignup ? 'Login' : 'Register'}</div>
</div>
)
}
else {
login = (
<div>
<p>Hello: {this.props.userId}</p>
<button>Logout</button>
</div>
)
}
if(this.props.loading) {
login = <div>Loading...</div>
}
return (
<div>
<div className="header-inner">
{this.props.tokenId}
{login}
<img src="http://civcic.com/assets/images/header-bg.jpg" alt="img" />
<div className="header-content">
<h2>React.JS DEVELOPER</h2>
<a className="knowmore-btn" href="https://www.upwork.com/freelancers/~01f507600be26cc2a3" rel="noopener noreferrer" target="_blank">Upwork profile</a><br />
<a className="knowmore-btn" href="https://www.linkedin.com/in/boris-civcic-37244378/" rel="noopener noreferrer" target="_blank">Linkedin</a><br />
<a className="knowmore-btn" href="https://github.com/fixman93" rel="noopener noreferrer" target="_blank">GitHub</a>
</div>
</div>
</div>
)
}
}
Header.defaultProps = {
tokenId: ''
}
Header.propTypes = {
tokenId: PropTypes.string
}
const mapStateToProps = state => {
return {
loading: state.auth.loading,
error: state.auth.error,
userId: state.auth.userId,
tokenId: state.auth.token
}
}
const mapDispatchToProps = dispatch => {
return {
onAuth: ( email, password, isSignup) => dispatch( actions.auth(email, password, isSignup))
}
}
export default connect(mapStateToProps, mapDispatchToProps)(Header)
import axios from 'axios';
import * as actionTypes from './actionsTypes';
export const authStart = () => {
return {
type: actionTypes.AUTH_START
}
}
export const authSuccess = (token, userId) => {
return {
type: actionTypes.AUTH_SUCCESS,
idToken: token,
userId: userId
}
}
export const authFail = (error) => {
return {
type: actionTypes.AUTH_FAIL,
error: error
};
};
export const auth = (email, password, isSignup) => {
return dispatch => {
dispatch(authStart());
const authData = {
email: email,
password: password,
fullName: 'Boris Civcic',
role: 'admin',
returnSecureToken: true
};
let url = 'https://www.googleapis.com/identitytoolkit/v3/relyingparty/signupNewUser?key=AIzaSyC5nW8-XOJADEvU7Mi7sgmhUNhHfRxXNQI';
if (!isSignup) {
url = 'https://www.googleapis.com/identitytoolkit/v3/relyingparty/verifyPassword?key=AIzaSyC5nW8-XOJADEvU7Mi7sgmhUNhHfRxXNQI';
}
axios.post(url, authData)
.then(response => {
console.log(response);
dispatch(authSuccess(response.data.idToken, response.data.localId));
// dispatch(checkAuthTime(response.data.expiresIn));
})
.catch(err => {
dispatch(authFail(err.response.data.error));
})
};
};
this is auth.js action
and this is utility
export const updateObject = (oldObject, updatedProperties) => {
return {
...oldObject,
...updatedProperties
};
};
In your register handler, onAuth is an asynchronous action but you've populated localStorage immediately. You should wait onAuth returns and then set your localStorage items.
first return a promise from your thunk ( simply by adding return before axios ):
...
return axios.post(url, authData)
.then(response => {
console.log(response);
dispatch(authSuccess(response.data.idToken, response.data.localId));
// dispatch(checkAuthTime(response.data.expiresIn));
})
.catch(err => {
dispatch(authFail(err.response.data.error));
})
...
Then set your localStorage items like this:
register = (event) => {
event.preventDefault();
this.props.onAuth( this.state.email, this.state.password, this.state.isSignup )
.then(() => {
localStorage.setItem('token', this.props.tokenId);
localStorage.setItem('userId', this.props.userId);
});
}
I made a page by using nodejs+react+redux.
I made a edit button in the page and want to edit my data.
Now I select KUMAMOTO-CTI and change the priority from 5 to 10.I use ajax to get data from database to fill the blank.Before I submit it I can get the right data from database.But when I change priority and click to edit it again.The filled data will be wrong.I have to refresh it to get the right data.
And sometimes it will become like this:Error
Could anybody tell me how to solve it?
Here is the pic:progress
Here is my code.
server.js
const Koa = require('koa')
const Router = require('koa-router')
const cors = require('koa2-cors')
const app = new Koa()
const router = new Router()
import { createStore } from 'redux'
import { Provider } from 'react-redux'
import serversReducer from '../client/reducers/reducer'
import { renderToString } from 'react-dom/server'
const store = createStore(serversReducer)
const views = require('koa-views')
const co = require('co')
const convert = require('koa-convert')
const json = require('koa-json')
const onerror = require('koa-onerror')
const bodyparser = require('koa-bodyparser')
const logger = require('koa-logger')
const debug = require('debug')('koa2:server')
const path = require('path')
//const ReactDOMServer = require('react-dom/server')
const React = require('react')
const config = require('./config')
const routes = require('./routes')
const mongoose = require('mongoose')
const stateSchema = require('./models/State')
const mission = require('./models/port')
const port = process.env.PORT || config.port
import Main from '../client/containers/Main'
mongoose.connect('mongodb://127.0.0.1:27017/monitor', {
useMongoClient: true
});
mongoose.Promise = global.Promise;
var State = mongoose.model("State", stateSchema);
// error handler
onerror(app)
// middlewares
app.use(bodyparser())
.use(json())
.use(logger())
.use(cors())
.use(require('koa-static')(__dirname + '/public'))
.use(views(path.join(__dirname, '/views'), {
options: {
settings: {
views: path.join(__dirname, 'views')
}
},
map: {
'ejs': 'ejs'
},
extension: 'ejs'
}))
.use(router.routes())
.use(router.allowedMethods())
// logger
app.use(async(ctx, next) => {
const start = new Date()
await next()
const ms = new Date() - start
console.log(`${ctx.method} ${ctx.url} - $ms`)
})
router.get('/', async(ctx, next) => {
const staticMarkup = await renderToString(
<Provider store={store}>
<Main />
</Provider>
)
const preloadedState = store.getState();
//console.log(preloadedState);
await ctx.render('index', {
reduxData: preloadedState,
helloComponentMarkup: staticMarkup
})
})
router.post('/show', async(ctx, next) => {
ctx.body = 'ok';
let newArray = [];
await State.find({}, function(err, doc) {
if (err) {
return;
}
doc.forEach(function(element, index) {
newArray.push(element);
})
ctx.response.body = JSON.stringify(newArray);
}).sort({
priority: 1
})
})
router.post('/edit', async(ctx, next) => {
ctx.body = 'ok'
await State.update({
_id: ctx.request.body.querymark
}, {
$set: {
server_name: ctx.request.body.servername,
jp_name: ctx.request.body.jpname,
ip_address: ctx.request.body.ipaddress,
port: ctx.request.body.port,
priority: ctx.request.body.priority
}
}, function(err, doc) {
if (err) {
return;
}
})
})
router.post('/create', async(ctx, next) => {
ctx.body = 'ok'
await new State({
server_name: ctx.request.body.servername,
jp_name: ctx.request.body.jpname,
ip_address: ctx.request.body.ipaddress,
port: ctx.request.body.port,
priority: ctx.request.body.priority
}).save(function(err) {
if (err)
console.log(err.toString());
})
})
router.post('/delete', async(ctx, next) => {
ctx.body = 'ok'
await State.remove({
_id: ctx.request.body.id
}, function(err, doc) {
if (err) {
return;
}
});
})
routes(router)
app.on('error', function(err, ctx) {
console.log(err)
logger.error('server error', err, ctx)
})
module.exports = app.listen(config.port, () => {
console.log(`Listening on http://localhost:${config.port}`)
console.log(__dirname);
})
ListContainer.js
import React, { Component } from 'react'
import PropTypes from 'prop-types'
import { connect } from 'react-redux'
import { Router, Route, hashHistory } from 'react-router'
import { Segment, Icon, Table, Modal, Button, Form } from 'semantic-ui-react'
const axios = require('axios')
class ListContainer extends Component {
static propTypes = {
test_data: PropTypes.object.isRequired,
post_data: PropTypes.object.isRequired,
onDeleteServer: PropTypes.func,
onEditServer: PropTypes.func,
initServers: PropTypes.func,
index: PropTypes.number
}
constructor(props) {
super(props)
this.state = ({
servername: props.post_data.server_name,
jpname: props.post_data.jp_name,
ipaddress: props.post_data.ip_address,
port: props.post_data.port,
priority: props.post_data.priority
})
}
static defaultProps = {
servers: []
}
handleDeleteServer(index) {
if (this.props.onDeleteServer) {
this.props.onDeleteServer(this.props.index)
} else {
console.log("error")
}
}
/*handleEditServer(index) {
if (this.props.onEditServer) {
this.props.onEditServer(this.props.index)
} else {
console.log("error")
}
this.setState({
open: false
})
}*/
handleServerNameChange(event) {
this.setState({
servername: event.target.value
})
}
handleJPNameChange(event) {
this.setState({
jpname: event.target.value
})
}
handleIPChange(event) {
this.setState({
ipaddress: event.target.value
})
}
handlePORTChange(event) {
this.setState({
port: event.target.value
})
}
handlePriorityChange(event) {
this.setState({
priority: event.target.value
})
}
handleSubmit(index) {
axios.post('/edit', {
querymark: this.props.post_data._id,
servername: this.state.servername,
jpname: this.state.jpname,
ipaddress: this.state.ipaddress,
port: this.state.port,
priority: this.state.priority
}).then((response) => {
var new_data;
if (response.data.success === false) {
alert("error")
} else {
window.location.reload();
//console.log(this.props.post_data)
dispatch(onEditServer(index, {
querymark: this.props.post_data._id,
servername: this.state.servername,
jpname: this.state.jpname,
ipaddress: this.state.ipaddress,
port: this.state.port,
priority: this.state.priority
}))
}
}).catch(() => {
})
this.setState({
open: false
})
}
state = {
open: false
}
show = (size, dimmer) => () => this.setState({
size,
dimmer,
open: true
})
close = () => this.setState({
open: false
})
render() {
const {open, size, dimmer} = this.state
const post_data = this.props.post_data
var updated_time = (new Date(post_data.updated_at)).toLocaleString().replace('/T/', '').replace('/\../+', '')
var state_color = (post_data.state == "green") ? "green" : "red"
var icon_name = (post_data.state == "green") ? "smile" : "warning sign"
return (
<Table.Row>
<Table.Cell><Icon name={icon_name} color={state_color}/></Table.Cell>
<Table.Cell>{post_data.jp_name}</Table.Cell>
<Table.Cell>{post_data.ip_address}</Table.Cell>
<Table.Cell>{post_data.port}</Table.Cell>
<Table.Cell>{updated_time}</Table.Cell>
<Table.Cell>{post_data.priority}</Table.Cell>
<Table.Cell>
<div>
<Icon link name='settings' color='purple' onClick={this.show('small', 'blurring')} />
<Modal size={size} dimmer={dimmer} open={open} onClose={this.close} closeIcon>
<Modal.Header>Edit</Modal.Header>
<Modal.Content>
<Modal.Description>
<Form>
<Form.Group width='equal'>
<Form.Field>
<label>Server Name</label>
<input value={this.state.servername} onChange={this.handleServerNameChange.bind(this)} />
</Form.Field>
<Form.Field>
<label>JP Name</label>
<input value={this.state.jpname} onChange={this.handleJPNameChange.bind(this)} />
</Form.Field>
</Form.Group>
<Form.Group width='equal'>
<Form.Field>
<label>IP Address</label>
<input value={this.state.ipaddress} onChange={this.handleIPChange.bind(this)} />
</Form.Field>
<Form.Field>
<label>Priority</label>
<input value={this.state.priority} onChange={this.handlePriorityChange.bind(this)} />
</Form.Field>
</Form.Group>
<Form.Group>
<Form.Field>
<label>Port</label>
<input value={this.state.port} onChange={this.handlePORTChange.bind(this)} />
</Form.Field>
</Form.Group>
</Form>
</Modal.Description>
</Modal.Content>
<Modal.Actions>
<Button color='black' onClick={this.close}>
Nope
</Button>
<Button positive icon='checkmark' labelPosition='right' content="Submit" onClick={this.handleSubmit.bind(this)} />
</Modal.Actions>
</Modal>
</div>
</Table.Cell>
<Table.Cell><Icon link name='trash' color='purple' onClick={this.handleDeleteServer.bind(this)} /></Table.Cell>
</Table.Row>
)
}
}
const mapStateToProps = (state) => {
return {
servers: state.servers
}
}
const mapDispatchToProps = (dispatch) => {
return {
onEditServer: (index, data) => {
dispatch(editServer(index, data))
},
}
}
export default connect(
mapStateToProps,
mapDispatchToProps
)(ListContainer)
SegmentContainer.js
import React, { Component } from 'react'
import PropTypes from 'prop-types'
import { connect } from 'react-redux'
import { Container, Loader, Table, Grid, Icon, Button, Modal, Form } from 'semantic-ui-react'
import SegmentList from '../components/SegmentContainer'
import { initServers, deleteServer, editServer } from '../reducers/reducer'
import MenuFix from '../components/Menu'
const axios = require('axios')
class SegmentContainer extends Component {
static propTypes = {
servers: PropTypes.array,
initServers: PropTypes.func,
onDeleteServer: PropTypes.func,
onAddServer: PropTypes.func
}
constructor() {
super()
//this._loadData()
this.state = {
servername: '',
jpname: '',
ipaddress: '',
priority: ''
}
}
componentWillMount() {
this._loadData()
}
componentDidMount() {
if (this.timer) {
clearInterval(this.timer)
}
this.timer = setInterval(() => {
this._loadData()
}, 3000)
}
componentWillUnmount() {
clearInterval(this.timer)
}
_loadData() {
let sorted_data = [];
let posts_data = [];
let response = axios.post('/show')
.then((response) => {
Object.keys(response.data).forEach(function(index) {
sorted_data.push(response.data[index]);
})
function _dataCompare(a, b) {
if (a.priority > b.priority)
return 1;
if (a.priority < b.priority)
return -1;
return 0;
}
sorted_data.forEach((item, index) => {
posts_data.push(item);
})
posts_data.sort(_dataCompare);
this.props.initServers(posts_data)
//dispatch(initServers(posts_data))
}).catch(() => {
})
}
handleDeleteServer(index) {
const {servers} = this.props
axios.post('/delete', {
id: servers[index]._id
}).then((response) => {
if (response.data.success === false) {
alert("error");
} else {
window.location.reload();
}
}).catch(() => {
})
if (this.props.onDeleteServer) {
this.props.onDeleteServer(index)
}
}
/*handleEditServer(index) {
const {servers} = this.props
axios.post('/edit', {
querymark: this.props.servers._id,
servername: this.state.servername,
jpname: this.state.jpname,
ipaddress: this.state.ipaddress,
port: this.state.port,
priority: this.state.priority
}).then((response) => {
if (response.data.success === false) {
alert("error")
} else {
//window.location.reload();
dispatch(onEditServer(index, {
querymark: this.props.servers._id,
servername: this.state.servername,
jpname: this.state.jpname,
ipaddress: this.state.ipaddress,
port: this.state.port,
priority: this.state.priority
}))
}
}).catch(() => {
})
this.setState({
open: false
})
}*/
handleCreate(server) {
axios.post('/create', {
servername: this.state.servername,
jpname: this.state.jpname,
ipaddress: this.state.ipaddress,
port: this.state.port,
priority: this.state.priority
}).then((response) => {
if (response.data.success === false) {
alert("error");
} else {
window.location.reload()
dispatch(onAddServer(index, {
servername: this.state.servername,
jpname: this.state.jpname,
ipaddress: this.state.ipaddress,
port: this.state.port,
priority: this.state.priority
}))
}
}).catch(() => {
})
this.setState({
open: false
})
}
handleServerNameChange(event) {
this.setState({
servername: event.target.value
})
}
handleJPNameChange(event) {
this.setState({
jpname: event.target.value
})
}
handleIPChange(event) {
this.setState({
ipaddress: event.target.value
})
}
handlePORTChange(event) {
this.setState({
port: event.target.value
})
}
handlePriorityChange(event) {
this.setState({
priority: event.target.value
})
}
state = {
open: false
}
show = (size, dimmer) => () => this.setState({
size,
dimmer,
open: true
})
close = () => this.setState({
open: false
})
render() {
const {open, size, dimmer} = this.state
return (
<Grid>
<MenuFix />
<Container style = {{
marginTop: '6em'
}}>
<Table unstackable>
<Table.Header>
<Table.Row>
<Table.HeaderCell colSpan='8'>
<Button basic color='violet' floated='right' icon labelPosition='left' primary size='tiny' onClick={this.show('small', 'blurring')}>
<Icon link color='violet' name='add' />Add
</Button>
<Modal size={size} dimmer={dimmer} open={open} onClose={this.close} closeIcon>
<Modal.Header>Add</Modal.Header>
<Modal.Content>
<Modal.Description>
<Form>
<Form.Group width='equal'>
<Form.Field>
<label>Server Name</label>
<input value={this.state.servername} onChange={this.handleServerNameChange.bind(this)} />
</Form.Field>
<Form.Field>
<label>JP Name</label>
<input value={this.state.jpname} onChange={this.handleJPNameChange.bind(this)} />
</Form.Field>
</Form.Group>
<Form.Group width='equal'>
<Form.Field>
<label>IP Address</label>
<input value={this.state.ipaddress} onChange={this.handleIPChange.bind(this)} />
</Form.Field>
<Form.Field>
<label>Priority</label>
<input value={this.state.priority} onChange={this.handlePriorityChange.bind(this)} />
</Form.Field>
</Form.Group>
<Form.Group>
<Form.Field>
<label>Port</label>
<input value={this.state.port} onChange={this.handlePORTChange.bind(this)} />
</Form.Field>
</Form.Group>
</Form>
</Modal.Description>
</Modal.Content>
<Modal.Actions>
<Button color='black' onClick={this.close}>
Nope
</Button>
<Button positive icon='checkmark' labelPosition='right' content="Submit" onClick={this.handleCreate.bind(this)} />
</Modal.Actions>
</Modal>
</Table.HeaderCell>
</Table.Row>
<Table.Row>
<Table.HeaderCell>State<Loader active inline size='small' /></Table.HeaderCell>
<Table.HeaderCell>Server Name</Table.HeaderCell>
<Table.HeaderCell>IP Address</Table.HeaderCell>
<Table.HeaderCell>Port</Table.HeaderCell>
<Table.HeaderCell>Updated</Table.HeaderCell>
<Table.HeaderCell>Priority</Table.HeaderCell>
<Table.HeaderCell>Edit</Table.HeaderCell>
<Table.HeaderCell>Delete</Table.HeaderCell>
</Table.Row>
</Table.Header>
<SegmentList posts_data = {this.props.servers} onDeleteServer={this.handleDeleteServer.bind(this)} />
</Table>
</Container>
</Grid>
)
}
}
const mapStateToProps = (state) => {
return {
servers: state.servers
}
}
const mapDispatchToProps = (dispatch) => {
return {
initServers: (servers) => {
dispatch(initServers(servers))
},
onDeleteServer: (index) => {
dispatch(deleteServer(index))
},
onEditServer: (index, data) => {
dispatch(editServer(index, data))
},
onAddServer: (server) => {
dispatch(addServer(server))
}
}
}
export default connect(
mapStateToProps,
mapDispatchToProps
)(SegmentContainer)
reducer.js
//action types
const INIT_SERVERS = 'INIT_SERVERS'
const ADD_SERVER = 'ADD_SERVER'
const DELETE_SERVER = 'DELETE_SERVER'
const EDIT_SERVER = 'EDIT_SERVER'
//reducer
export default function(state, action) {
if (!state) {
state = {
servers: []
}
}
switch (action.type) {
case INIT_SERVERS:
return {
servers: action.servers
}
case ADD_SERVER:
return {
servers: [...state.servers, action.server]
}
case EDIT_SERVER:
return {
servers: [...state.servers.slice(0, action.index), Object.assign({}, state.servers[action.index], {
done: true
}), ...state.servers.slice(action.index + 1)]
/*servers: Object.assign([...state.servers], {
[index]: action.data
})*/
//servers: Object.assign({}, state[action.index], action.data)
}
case DELETE_SERVER:
return {
servers: [
...state.servers.slice(0, action.index),
...state.servers.slice(action.index + 1)
]
}
default:
return state
}
}
// action creators
export const initServers = (servers) => {
return {
type: INIT_SERVERS,
servers
}
}
export const addServer = (server) => {
return {
type: ADD_SERVER,
server
}
}
export const deleteServer = (index) => {
return {
type: DELETE_SERVER,
index
}
}
export const editServer = (index, data) => {
return {
type: EDIT_SERVER,
index,
data
}
}
SegmentList.js
import React, { Component, PropTypes } from 'react'
import ListContainer from '../containers/ListContainer'
export default class SegmentList extends Component {
static propTypes = {
posts_data: PropTypes.array,
onDeleteServer: PropTypes.func,
//onEditServer: PropTypes.func
}
static defaultProps = {
posts_data: []
}
handleDeleteServer(index) {
if (this.props.onDeleteServer) {
this.props.onDeleteServer(index)
}
}
/*handleEditServer(index) {
if (this.props.onEditServer) {
this.props.onEditServer(index)
}
}*/
render() {
return (
<div>
{this.props.posts_data.map((data, i) => <ListContainer
post_data={data}
key={i}
index={i} onDeleteServer={this.handleDeleteServer.bind(this)} />
)}
</div>
)
}
}
First of all, react is for SPA, if you use reload just use HTTP (no ajax) to edit. Here is solution for SPA
before coding, the main idea is to update your data after edit, so the road map should be:
1.fetch an array from server and store into state.servers, and it should be a list of plain objects that contain your server info
2.when you update, you should apply the update to server and then update to local store, as you did in create/delete, edit is most likely the same thing with more data
then your handleSubmit should be (notice index from iteration as param)
//index from iteration, should be passed from parent component
handleSubmit(index) {
const data = {
querymark: this.props.post_data._id,
servername: this.state.servername,
jpname: this.state.jpname,
ipaddress: this.state.ipaddress,
port: this.state.port,
priority: this.state.priority
}
axios.post('/edit', data).then((response) => {
if (response.data.success === false) {
alert("error");
} else {
//window.location.reload();
dispatch(onEditServer(index,data))
}
}).catch(() => {})
this.setState({
open: false
})
}
onEditServer should be
const mapDispatchToProps = (dispatch) => {
return {
initServers: (servers) => {
dispatch(initServers(servers))
},
onDeleteServer: (serverIndex) => {
dispatch(deleteServer(serverIndex))
},
onEditServer: (index, data) => {
dispatch(editServer({
type: 'edit',
index,
data,
}))
},
onAddServer: (server) => {
dispatch(addServer(server))
}
}
}
edit reducer
// edit reducer
const edit = (state, action) {
state.servers[action.index] = actions.data
return state
}
then changes will take effect automatically