Skip to content

Commit

Permalink
Refactor/function based components (#23)
Browse files Browse the repository at this point in the history
* chore: Use airbnb/hooks to lint React hooks

* refactor: Events index is function component

The redux app state has several branches, including the "events" branch.
Previously, this "events" branch was provided as props to the Events component through the the `connect` call and its `mapStateToProps` argument.
Another prop provided by the `connect` call was the `getAll()` prop.
This prop didn't rely on a branch of the app state; instead it was set up to dispatch actions which were then handled by the redux action handlers.

We still select the same "events" branch of the app state, but do so within the component itself through the `useSelector` call, which receives the entire app state, and slices-off and returns the "events" branch.

We still dispatch the same "getAll" action when the component mounts, but do so through the `useDispatch` call, which returns the redux dispatcher directly.

Instead of using the "componentDidMount" Component lifecycle hook, we use the `useEffect` hook to run the hook once, when the component is first rendered.

The second argument to "connect" was a mapping of action creators to prop names. Within that call, redux wrapped each of these action creators with the equivalent of `dispatch(…)`.
Now, we have access to `dispatch` directly, through the `useDispatch` hook.

* WIP Event edit

* WIP

* WIP

* WIP

* WIP

---------

Co-authored-by: Duncan Beevers <[email protected]>
  • Loading branch information
zyphlar and duncanbeevers authored Apr 29, 2023
1 parent 5502121 commit 8cfe47d
Show file tree
Hide file tree
Showing 11 changed files with 1,433 additions and 65 deletions.
5 changes: 3 additions & 2 deletions .eslintrc
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
// enable airbnb eslint from https://www.npmjs.com/package/eslint-config-airbnb
"extends": "airbnb",
"extends": ["airbnb", "airbnb/hooks"],
"globals": {
"window": true,
"document": true,
Expand All @@ -25,6 +25,7 @@
"import/prefer-default-export": 0,
"react/jsx-boolean-value": 0,
"react/jsx-filename-extension": 0,
"react/forbid-prop-types": 0
"react/forbid-prop-types": 0,
"react/prop-types": 0,
}
}
1,106 changes: 1,106 additions & 0 deletions package-lock.json

Large diffs are not rendered by default.

1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
"lodash": "^4.17.21",
"react": "^18.2.0",
"react-dom": "^18.2.0",
"react-hook-form": "7.43.9",
"react-redux": "^8.0.5",
"react-router-dom": "^6.6.1",
"react-scripts": "5.0.1",
Expand Down
25 changes: 25 additions & 0 deletions src/components/form/FormInputText.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
import React, { useCallback } from 'react';
import { Controller, useFormContext } from 'react-hook-form';
import { TextField } from '@mui/material';

export default function FormInputText({ name, label }) {
const { control } = useFormContext();

const handleRender = useCallback(({ field: { value, onChange }, fieldState: { error } }) => {
return (
<TextField
name={name}
label={label}
error={!!error}
value={value}
fullWidth={true}
variant="outlined"
onChange={onChange}
/>
);
}, [label, name]);

return (
<Controller name={name} control={control} render={handleRender} />
);
}
2 changes: 2 additions & 0 deletions src/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ import { verify } from './state/user';
import './index.css';
import theme from './theme';
import AuthWrapper from './components/auth-wrapper';
import EventEdit from './pages/event_edit';

// @ts-ignore
const composeEnhancers = window.__REX_DEVTOOLS_EXTENSION_COMPOSE__ || compose;
Expand Down Expand Up @@ -60,6 +61,7 @@ root.render(
<Route path="/users/:id" element={<AuthWrapper><UserDetails /></AuthWrapper>} />
<Route path="/events" element={<AuthWrapper><Events /></AuthWrapper>} />
<Route path="/events/:event_id" element={<AuthWrapper><Event /></AuthWrapper>} />
<Route path="/events/:event_id/edit" element={<AuthWrapper><EventEdit /></AuthWrapper>} />
<Route path="/certs" element={<AuthWrapper><Certs /></AuthWrapper>}/>
<Route path="/certs/:id" element={<AuthWrapper><CertDetails /></AuthWrapper>} />
</Routes>
Expand Down
2 changes: 1 addition & 1 deletion src/pages/event.js
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ class App extends Component {
// const event = this.props.getOne(1); //this.props.match.params.event_id

let eventList = '';
const ev = events.one;
const ev = events.getOne;

if (ev) {
const dateRange = formatDateRange(ev);
Expand Down
244 changes: 244 additions & 0 deletions src/pages/event_edit.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,244 @@
/* eslint-disable react/jsx-props-no-spreading */
import React, { useEffect } from 'react';
import { useDispatch, useSelector } from 'react-redux';
import { useParams } from 'react-router-dom';
import { FormProvider, useForm } from 'react-hook-form';
import { CircularProgress, Paper } from '@mui/material';
import { edit, getOne } from '../state/events';
import FormInputText from '../components/form/FormInputText';
import Header from '../components/header';
// import { connect } from 'react-redux';
// import { Link } from 'react-router-dom';
// import CircularProgress from '@material-ui/core/CircularProgress';
// import TextField from '@material-ui/core/TextField';
// import Button from '@material-ui/core/Button';
// import { DataGrid } from '@material-ui/data-grid';
// import Header from '../components/header';
// import { read, edit, add } from '../state/events';

// const styles = {
// eventDescription: {
// whiteSpace: 'pre-line',
// },
// tableContainer: {
// // margin: '10px',
// },
// gridContainer: {
// width: '100%',
// display: 'flex',
// height: 450,
// },
// nameField: {
// fontSize: '1.5em',
// magin: '5px',
// },
// descriptionFeild: {
// width: '125ch',
// magin: '5px',
// },
// form: {
// width: '90%',
// margin: '5px',
// },
// formButton: {
// margin: '5px',
// },
// };

// type FetchState = 'idle' | 'pending' | 'success' | 'error';

// function useFetch(fn) {
// const [data, setData] = useState();

// const [fetchState, setFetchState] = useState<FetchState>('idle');

// const fetch = useCallback(() => {
// fn().then(setData);
// }, []);

// return { data, fetch, isPending: fetchState === 'pending' };
// }

export default function EventEdit() {
const params = useParams();
const event = useSelector(({ events: { getOne: event } }) => event);

const dispatch = useDispatch();

useEffect(() => {
dispatch(getOne(params.event_id));
}, [dispatch, params.event_id]);

const methods = useForm({
defaultValues: {
name: '',
},
});

const { reset } = methods;

useEffect(() => {
reset(event);
}, [event, reset]);

const onSubmit = (updatedEvent) => {
dispatch(edit(params.event_id, {
name: updatedEvent.name,
description: updatedEvent.description,
start_date: updatedEvent.start_date,
end_date: updatedEvent.end_date,
location: updatedEvent.location,
frequency: updatedEvent.frequency,
}));
};

if (!event) {
return <CircularProgress />;
}

return (
<>
<Header />
<Paper>

<FormProvider {...methods}>
<form id="eventForm" onSubmit={methods.handleSubmit(onSubmit)}>
<FormInputText name="name" label="Name" />
</form>
</FormProvider>

Hello
<pre>{JSON.stringify(event, null, 2)}</pre>
<button form="eventForm" type="submit">Submit</button>

{/* <button type="button" onClick={reset(onSubmit)}>Reset</button> */}
</Paper>
</>
);
}

// class Events extends Component {
// state = {
// name: '',
// frequency: '',
// location: '',
// description: '',
// start_date: '',
// end_date: ''
// };

// nameChange = (e) => {
// this.setState({name: e.target.value});
// };

// frequencyChange = (e) => {
// this.setState({frequency: e.target.value});
// };

// locationChange = (e) => {
// this.setState({location: e.target.value});
// };

// descriptionChange = (e) => {
// this.setState({description: e.target.value});
// };

// startDateChange = (e) => {
// this.setState({start_date: e.target.value});
// };

// endDateChange = (e) => {
// this.setState({end_date: e.target.value});
// };

// submitForm = () => {
// const { event_id } = this.props.match.params;
// const { name, frequency, description } = this.state;
// this.props.edit(event_id, { name, frequency, description });
// };

// async componentDidMount(){
// const { event_id } = this.props.match.params;
// const { id, name, frequency, location, description, start_date, end_date } = await this.props.read(event_id);
// this.setState({ id, name, frequency, location, description, start_date, end_date });
// }

// render() {

// const { read } = this.props.events;
// const { auth } = this.props.user;
// return (
// <div style={styles.container}>
// <Header/>
// {auth.isAdmin && read ? (
// <div style={styles.form}>
// <TextField
// label="Name"
// style={styles.nameField}
// value={this.state.name}
// onChange={this.nameChange}
// />
// <br/>
// <TextField
// label="Frequency"
// style={styles.frequencyField}
// value={this.state.frequency}
// onChange={this.frequencyChange}
// />
// <br/>
// <TextField
// label="Location"
// style={styles.locationField}
// value={this.state.location}
// onChange={this.locationChange}
// />
// <br/>
// <TextField
// label="Description"
// fullWidth
// multiline
// style={styles.nameField}
// value={this.state.description}
// onChange={this.descriptionChange}
// />
// <TextField
// label="Start Date"
// style={styles.startDateField}
// value={this.state.start_date}
// onChange={this.startDateChange}
// />
// <TextField
// label="End Date"
// style={styles.endDateField}
// value={this.state.end_date}
// onChange={this.endDateChange}
// />
// <br/>
// <Button
// style={styles.formButton}
// variant="contained"
// color="primary"
// onClick={this.submitForm}
// >
// Update
// </Button>
// <Link to={`/events/${this.state.id}`}>Back</Link>
// </div>
// ) : (
// <>
// <h1>{read ? read.name : ''}</h1>
// <h2>{read ? read.description : ''}</h2>
// </>
// )}

// </div>
// );
// }
// }

// function mapStateToProps(state) {
// const { events } = state;
// return { events };
// }

// export default connect(mapStateToProps, { read, edit, add })(Events);
Loading

0 comments on commit 8cfe47d

Please sign in to comment.