Skip to content

Instantly share code, notes, and snippets.

@fuentesld
Created July 25, 2019 21:53
Show Gist options
  • Select an option

  • Save fuentesld/b9778f4ed3ac817482c021a867dc88a2 to your computer and use it in GitHub Desktop.

Select an option

Save fuentesld/b9778f4ed3ac817482c021a867dc88a2 to your computer and use it in GitHub Desktop.
React Context Boilerplate

React Context Boilerplate

Create React app

npx create-react-app <app-name>

File system

/
|-src-/
      |
      |-context -/
      |          |-types.js
      |          |-client-/
      |          |         |- ClientcontState.js
      |          |         |- contactContext.js
      |          |         |- contactReducer.js
      |          |
      |          |-(More contexts...)
      |
      |-components -/
      |             |-clients-/
      |             |         |- Client.js
      |             |         |- ClientItem.js
      |             |         |- (more ClientOpt...)
                                  
        

la Apliación completa se encuentra en el repositorio Client-Keeper

import React, { Fragment, useContext, useEffect } from "react";
import { CSSTransition, TransitionGroup } from "react-transition-group";
import ClientContext from "../../context/client/clientContext";
import ClientItem from "./ClientItem";
import Spinner from "../layout/Spinner";
const ClienteItem = () => {
const clientContext = useContext(ClientContext);
const { clients, filtered, getClients, loading } = clientContext;
useEffect(() => {
getClients();
// eslint-disable-next-line
}, []);
if (clients !== null && clients.length === 0 && !loading) {
return <h4>Añada un contacto por favor ...</h4>;
}
return (
<Fragment>
{clients !== null && !loading ? (
<TransitionGroup>
{filtered != null
? filtered.map(client => (
<CSSTransition key={client._id} timeout={500} className="item">
<ClientItem key={client._id} client={client} />
</CSSTransition>
))
: clients.map(client => (
<CSSTransition key={client._id} timeout={500} className="item">
<ClientItem key={client._id} client={client} />
</CSSTransition>
))}
</TransitionGroup>
) : (
<Spinner />
)}
</Fragment>
);
};
export default ClienteItem;
import { createContext } from "react";
const clientContext = createContext();
export default clientContext;
import React, { useContext } from "react";
import PropTypes from "prop-types";
import ClientContext from "../../context/client/clientContext";
const ClienteItem = ({ client }) => {
const clientContext = useContext(ClientContext);
const { deleteClient, setCurrent, clearCurrent } = clientContext;
const { _id, name, email, phone, type } = client;
const onDelete = () => {
deleteClient(_id);
clearCurrent();
};
const onEdit = () => {
setCurrent(client);
};
return (
<div className="card">
<h4 className="text-left">
{name}{" "}
<span
style={{ float: "right" }}
className={
"badge " + (type === "VIP" ? "badge-success" : "badge-primary")
}
>
{type.charAt(0).toUpperCase() + type.slice(1)}
</span>
</h4>
<ul className="list-group list-group-flush">
{email && (
<li className="list-group-item p-1">
<i className="fas fa-envelope-open" /> {email}
</li>
)}
{phone && (
<li className="list-group-item p-1 mb-2">
<i className="fas fa-phone" /> {phone}
</li>
)}
</ul>
<p className="my-1">
<button className="btn btn-primary btn-sm mx-1" onClick={onEdit}>
Editar
</button>
<button className="btn btn-danger btn-sm" onClick={onDelete}>
Borrar
</button>
</p>
</div>
);
};
ClienteItem.propTypes = {
client: PropTypes.object.isRequired
};
export default ClienteItem;
import {
ADD_CLIENT,
UPDATE_CLIENT,
DELETE_CLIENT,
SET_CURRENT,
CLEAR_CURRENT,
FILTER_CLIENTS,
CLEAR_FILTER,
CLIENT_ERROR,
GET_CLIENTS,
CLEAR_CLIENTS
} from "../types";
export default (state, action) => {
switch (action.type) {
case GET_CLIENTS:
return {
...state,
clients: action.payload,
loading: false
};
case ADD_CLIENT:
return {
...state,
clients: [action.payload, ...state.clients],
loading: false
};
case UPDATE_CLIENT:
return {
...state,
clients: state.clients.map(client =>
client._id === action.payload._id ? action.payload : client
),
loading: false
};
case DELETE_CLIENT:
return {
...state,
clients: state.clients.filter(client => client._id !== action.payload),
loading: false
};
case CLEAR_CLIENTS:
return {
...state,
clients: null,
filtered: null,
error: null,
current: null
};
case SET_CURRENT:
return {
...state,
current: action.payload
};
case CLEAR_CURRENT:
return {
...state,
current: null
};
case FILTER_CLIENTS:
return {
...state,
filtered: state.clients.filter(client => {
const regex = new RegExp(`${action.payload}`, "gi");
return (
client.name.match(regex) ||
client.email.match(regex) ||
client.phone.match(regex) ||
client.type.match(regex)
);
})
};
case CLEAR_FILTER:
return {
...state,
filtered: null
};
case CLIENT_ERROR:
return {
...state,
error: action.payload
};
default:
return state;
}
};
import React, { useReducer } from "react";
import axios from "axios";
import ClientContext from "./clientContext";
import clientReducer from "./clientReducer";
import {
ADD_CLIENT,
UPDATE_CLIENT,
DELETE_CLIENT,
SET_CURRENT,
CLEAR_CURRENT,
FILTER_CLIENTS,
CLEAR_FILTER,
CLIENT_ERROR,
GET_CLIENTS,
CLEAR_CLIENTS
} from "../types";
const ClientState = props => {
const initialState = {
clients: null,
current: null,
filtered: null,
error: null
};
const [state, dispatch] = useReducer(clientReducer, initialState);
// Get Clients
const getClients = async () => {
try {
const res = await axios.get("/api/clients");
dispatch({ type: GET_CLIENTS, payload: res.data });
} catch (error) {
dispatch({ type: CLIENT_ERROR, paylod: error.response.msg });
}
};
// Add Client
const addClient = async client => {
const config = {
headers: {
"Content-Type": "application/json"
}
};
try {
const res = await axios.post("/api/clients", client, config);
dispatch({ type: ADD_CLIENT, payload: res.data });
} catch (error) {
dispatch({ type: CLIENT_ERROR, paylod: error.response.msg });
}
};
// Update Client
const updateClient = async client => {
const config = {
headers: {
"Content-Type": "application/json"
}
};
try {
const res = await axios.put(`/api/clients/${client._id}`, client, config);
dispatch({ type: UPDATE_CLIENT, payload: res.data.client });
} catch (error) {
dispatch({ type: CLIENT_ERROR, paylod: error.response.msg });
}
};
// Delete Client
const deleteClient = async id => {
try {
await axios.delete(`/api/clients/${id}`);
dispatch({ type: DELETE_CLIENT, payload: id });
} catch (error) {
dispatch({ type: CLIENT_ERROR, paylod: error.response.msg });
}
};
// Clear Clients
const clearClients = () => {
dispatch({ type: CLEAR_CLIENTS });
};
// Set Current Client
const setCurrent = client => {
dispatch({ type: SET_CURRENT, payload: client });
};
// Clear Current Client
const clearCurrent = client => {
dispatch({ type: CLEAR_CURRENT });
};
// Filter Clients
const filterClients = text => {
dispatch({ type: FILTER_CLIENTS, payload: text });
};
// Clear Filter
const clearFilter = () => {
dispatch({ type: CLEAR_FILTER });
};
return (
<ClientContext.Provider
value={{
clients: state.clients,
current: state.current,
filtered: state.filtered,
error: state.error,
addClient,
deleteClient,
updateClient,
setCurrent,
clearCurrent,
filterClients,
clearFilter,
getClients,
clearClients
}}
>
{props.children}
</ClientContext.Provider>
);
};
export default ClientState;
import React, { Fragment } from "react";
import { BrowserRouter as Router, Route, Switch } from "react-router-dom";
import ClientState from "./context/client/ClientState";
import AuthState from "./context/auth/AuthState";
import AlertState from "./context/alert/AlertState";
import Navbar from "./components/layout/Navbar";
import Home from "./components/pages/Home";
import About from "./components/pages/About";
import Register from "./components/auth/Register";
import Login from "./components/auth/Login";
import Alerts from "./components/layout/Alerts";
import PrivateRoute from "./components/routing/PrivateRoute";
import setAuthToken from "./utils/setAuthToken";
const App = () => {
if (localStorage.token) setAuthToken(localStorage.token);
return (
<AuthState>
<ClientState>
<AlertState>
<Router>
<Fragment>
<Navbar />
<div className="container">
<Alerts />
<Switch>
<PrivateRoute exact path="/" component={Home} />
<Route exact path="/about" component={About} />
<Route exact path="/register" component={Register} />
<Route exact path="/login" component={Login} />
</Switch>
</div>
</Fragment>
</Router>
</AlertState>
</ClientState>
</AuthState>
);
};
export default App;
export const ADD_CLIENT = "ADD_CLIENT";
export const UPDATE_CLIENT = "UPDATE_CLIENT";
export const DELETE_CLIENT = "DELETE_CLIENT";
export const SET_CURRENT = "SET_CURRENT";
export const CLEAR_CURRENT = "CLEAR_CURRENT";
export const FILTER_CLIENTS = "FILTER_CLIENTS";
export const CLEAR_FILTER = "CLEAR_FILTER";
// Clients types
export const GET_CLIENTS = "GET_CLIENTS";
export const CLEAR_CLIENTS = "CLEAR_CLIENTS";
export const CLIENT_ERROR = "CONTACT_ERROR";
// AUTH types
export const SET_ALERT = "SET_ALERT";
export const REMOVE_ALERT = "REMOVE_ALERT";
export const REGISTER_SUCCESS = "REGISTER_SUCCESS";
export const REGISTER_FAIL = "REGISTER_FAIL";
export const USER_LOADED = "USER_LOADED";
export const AUTH_ERROR = "AUTH_ERROR";
export const LOGIN_SUCCESS = "LOGIN_SUCCESS";
export const LOGIN_FAIL = "LOGIN_FAIL";
export const LOGOUT = "LOGOUT";
export const CLEAR_ERRORS = "CLEAR_ERRORS";
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment