对应内容:#17 Adding Event Features | Build a Complete App with GraphQL, Node.js, MongoDB and React.js
主要内容
- 抽离EventList 和 EventItem的逻辑,创建对应的组件
- 修改登录时的Bug
- 创建Modal查看Event细节的功能
- 创建一个Snipper
- 学习了函数组件中useContext的用法
1 抽离EventList 和 EventItem的逻辑,创建对应的组件,学习了函数组件中useContext的用法
EventList.js
import EventItem from './EventItem/EventItem';
const EventList = props => {
const eventItemList = props.events.map(event => {
return <EventItem
event={event}
key={event._id}
showDetail={props.showDetail}
/>
});
return (
<ul className="eventlist">{eventItemList}</ul>
);
}
export default EventList;
EventItem.js
https://zh-hans.reactjs.org/docs/hooks-reference.html#usecontext
const ctx = React.useContext(…React.createContext());
import React from 'react';
import './EventItem.css';
import AuthContext from '../../../../context/auth-context';
const EventItem = props => {
const ctx = React.useContext(AuthContext);
return (
<li className="eventlist__item">
<div>
<h1>{props.event.title}</h1>
<p className="text--highlight">price: {props.event.price}$ - {new Date(props.event.date).toLocaleString()}</p>
</div>
<div>
{
ctx.userId === props.event.creator._id ?
<p>you are the creator.</p>
: <div>
<button onClick={props.showDetail.bind(null, props.event._id)}>MORE DETAIL</button>
</div>
}
</div>
</li>
);
}
export default EventItem;
组件抽象做的不是很好,导致每次一改,就要大event.js。
import React, { Component } from 'react';
import Modal from '../components/Modal/Modal';
import Dropback from '../components/Dropback/Dropback';
import AuthContext from '../context/auth-context';
import EventList from '../components/Events/EventList/EventList';
import Snipper from '../components/Snipper/Snipper';
class EventsPage extends Component {
state = {
isModalShow: false,
isLoading: false,
events: [],
selectedEvent: null
}
static contextType = AuthContext;
constructor (props) {
super(props);
this.titleElRef = React.createRef();
this.dateElRef = React.createRef();
this.priceElRef = React.createRef();
this.descriptionElRef = React.createRef();
}
componentDidMount () {
this.fetchEvents();
}
modalStateSwitchHandler = () => {
this.setState({
isModalShow: true,
})
};
onCancel = () => {
this.setState({
isModalShow: false,
selectedEvent: null
})
};
onBook = () => {}
onConfirm = () => {
const title = this.titleElRef.current.value;
const date = this.dateElRef.current.value;
const price = this.priceElRef.current.value;
const description = this.descriptionElRef.current.value;
const queryBody = {
query: `
mutation {
createEvent(eventInput: {title: "${title}", price: ${price}, date: "${date}", description: "${description}"}) {
_id
title
price
date
description
creator {
_id
email
}
}
}
`
}
const token = this.context.token;
fetch("http://localhost:3030/graphql", {
method: "POST",
headers: {
"Content-Type": "application/json",
"Authorization": "Bearer " + token
},
body: JSON.stringify(queryBody),
})
.then(response => {
if (response.status !== 200 && response.status !== 201) {
throw new Error("request failed!")
}
this.setState({
isModalShow: false
})
return response.json();
})
.then(data => {
let newEvents = this.state.events;
this.setState({
events: newEvents.concat([data.data.createEvent])
})
})
.catch(err => {
throw err;
}) ;
};
/**
* fetch all events
*/
fetchEvents () {
this.setState({
isLoading: true
});
const queryBody = {
query: `
query {
events {
_id
title
price
date
description
creator {
_id
}
}
}
`
}
fetch("http://localhost:3030/graphql", {
method: "POST",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify(queryBody),
})
.then((response) => {
if (response.status !== 200 && response.status !== 201) {
throw new Error("request failed!");
}
return response.json();
})
.then((data) => {
this.setState({
events: data.data.events
})
})
.catch((err) => {
throw err;
})
.then(() => {
this.setState({
isLoading: false
});
},err => {
this.setState({
isLoading: false
});
throw err;
});
}
showDetailHandler = (eventId) => {
this.setState(prevState => {
const event = prevState.events.find(event => event._id === eventId);
return {
selectedEvent: event
}
})
}
render() {
return (
<React.Fragment>
{this.state.isModalShow && <Dropback />}
{this.state.isModalShow && <Modal
title="Add an event"
onCancel={this.onCancel}
onConfirm={this.onConfirm}
secondBtnText="Confirm"
>
<form>
<div className="form-control">
<label htmlFor="title">Title</label>
<input type="text" id="title" ref={this.titleElRef} />
</div>
<div className="form-control">
<label htmlFor="date">Date</label>
<input type="datetime-local" id="date" ref={this.dateElRef} />
</div>
<div className="form-control">
<label htmlFor="price">Price</label>
<input type="number" id="price" ref={this.priceElRef} />
</div>
<div className="form-control">
<label htmlFor="descrip">Description</label>
<textarea id="descrip" rows="4" ref={this.descriptionElRef} >
</textarea>
</div>
</form>
</Modal>}
{this.state.selectedEvent && <Modal
title="Event Detail"
onCancel={this.onCancel}
onConfirm={this.onBook}
secondBtnText="Book"
>
<h1>{this.state.selectedEvent.title}</h1>
<h2 className="text--highlight">
price: {this.state.selectedEvent.price}$ - {new Date(this.state.selectedEvent.date).toLocaleString()}
</h2>
<p>
{this.state.selectedEvent.description}
</p>
</Modal>}
<div>
<h1>The events Page</h1>
{this.context.token && <button onClick={this.modalStateSwitchHandler}>Add Event</button>}
</div>
{
this.state.isLoading ? <Snipper />
: <div>
<EventList events={this.state.events} showDetail={this.showDetailHandler}/>
</div>
}
</React.Fragment>
);
}
}
export default EventsPage;
2 登录时的BUG
3 Snipper
https://loading.io/css