How to change this class base Context API to reach Hook without changing other components that already consumed it? I am new to react and spend all night and I got stuck.
The actual code is more than this but I'm trying to remove some of the code for simplicity purposes:
import React, { createContext, Component } from 'react'
const MainContext = createContext();
class MainContextProvider extends Component {
state = {
isLogin : false,
loginData : [],
spinner : false
}
handleUserLogin = (res) => {
this.setState({
...this.state,
isLogin : res.isLogin,
loginData : res.data
})
}
showSpinner = (status) => {
this.setState({
...this.state,
spinner : status
})
}
render() {
console.log(this.state)
return (
<MainContext.Provider value = {{
...this.state,
showSpinner : this.showSpinner,
handleUserLogin : this.handleUserLogin,
}}>
{this.props.children}
</MainContext.Provider>
);
}
}
const MainContextConsumer = MainContext.Consumer;
export {MainContextProvider, MainContextConsumer, MainContext};
I wrap index.js with this MainContextProvider so all components can consume the states or use the methods.
Here is how to use context
with hooks and keeping the same API as what you already have:
import React, { createContext, useContext, useState } from "react";
import "./style.css";
// Define your context, this is the same
const MainContext = createContext();
function Provider({ children }) {
// Define some state to hold the data
let [state, setState] = useState({
isLogin: false,
loginData: [],
spinner: false
});
// Define a few functions that change the state
let handleUserLogin = res => {
setState(s => ({
...s,
isLogin: res.isLogin,
loginData: res.data
}));
};
// Define a few functions that change the state
let showSpinner = status => {
setState(s => ({ ...s, spinner: status }));
};
// Pass the `state` and `functions` to the context value
return (
<MainContext.Provider
value={{ ...state, handleUserLogin, showSpinner }}
>
{children}
</MainContext.Provider>
);
}
function Stuff() {
// Inside your component use the context with `useContext` hook
let { showSpinner, handleUserLogin, ...state } = useContext(MainContext);
return (
<div>
<div>
<code>{JSON.stringify(state, null, 2)}</code>
</div>
<button onClick={() => showSpinner(Math.random())}>
Show Spinner
</button>
</div>
);
}
export default function App() {
return (
<Provider>
<Stuff />
</Provider>
);
}
See the demo on StackBlitz
As Sam R. suggestion, I make little modification and works as expected. Maybe it's better to use Reducer but I prefer not. And I think Context API is more simple compare to Redux.
MainContext.js :
import React, { createContext, useState } from 'react'
const MainContext = createContext();
const MainContextProvider = ({ children }) => {
// Define some state to hold the data
let [state, setState] = useState({
isLogin: false,
loginData: [],
spinner: false
});
// Define a few functions that change the state
let handleUserLogin = res => {
setState(s => ({
...s,
isLogin: res.isLogin,
loginData: res.data
}));
};
// Define a few functions that change the state
let showSpinner = status => {
setState(s => ({ ...s, spinner: status }));
};
// Pass the `state` and `functions` to the context value
return (
<MainContext.Provider
value={{ ...state, handleUserLogin, showSpinner }}
>
{children}
</MainContext.Provider>
);
}
const MainContextConsumer = MainContext.Consumer;
export {MainContextProvider, MainContextConsumer, MainContext};
Login.js :
import React, { useState, useContext } from "react";
import { Link } from "react-router-dom";
import { useHistory } from "react-router-dom";
import {MainContext} from "../contextApi/MainContext";
import { login } from "../api/Api_User";
const Login = () => {
const history = useHistory();
const { handleUserLogin, showSpinner } = useContext(MainContext);
const [user , setUser] = useState({ email : "", password : "" })
const [errors , setErrors] = useState({ emailErr : "", passErr : "" })
const handleChange = e => {
const {name , value} = e.target
setUser( prevState => ({ ...prevState,[name] : value }))
setErrors({ emailErr : "", passErr : "" });
}
const handleSubmit = (e) => {
// client side validation
if(!user.email) { setErrors({ emailErr : "Please enter email" }); return false; }
if(!user.password) { setErrors({ passErr : "Please enter password" }); return false; }
showSpinner(true)
const data = {
email: user.email,
password: user.password
}
// axios call
login(data).then(res => {
setTimeout(() => {
showSpinner(false)
if (res) {
if (res.status === true) {
localStorage.setItem("token", res.token); // jwt token from server
handleUserLogin(res) // store server respond to global states
return history.push('/dashboard')
}
// server side validation
if (res.status === false) {
res.path === 'email' && setErrors({ emailErr : res.message })
res.path === 'password' && setErrors({ passErr : res.message })
}
}
},100 )
});
}
return (
<div className="page">
<div className="page-content mt-5 mb-5">
<div className="content-sticky-footer">
<div className="container">
<div className="row">
<div className="col">
<div className="card mb-0">
<div className="card-header">
<h3 className="mx-auto mt-4">LOGIN MEMBER</h3>
</div>
<div className="card-body">
<div className="form-group">
<label>Email address *</label>
<input
type="email" className="form-control"
name="email"
value={user.email}
onChange={handleChange}
/>
<span className="text-danger label-sm ">
{errors.emailErr}
</span>
</div>
<div className="form-group">
<label>Password *</label>
<input
type="password" className="form-control"
name="password"
value={user.password}
onChange={handleChange}
/>
<span className="text-danger label-sm ">
{errors.passErr}
</span>
</div>
<div className="form-footer mt-2">
<button
type="button"
className="btn btn-primary btn-block btn-lg btn-submit"
onClick={handleSubmit}
>
Login
</button>
</div>
<div className="text-center mt-3 text-dark">
Do not have account?
<Link to="/register"> Register</Link>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
);
}
export default Login
Index.js :
import React from 'react';
import ReactDOM from 'react-dom';
import 'bootstrap/dist/css/bootstrap.min.css';
import './css/App.css';
import App from './App';
import { BrowserRouter} from "react-router-dom";
import {MainContextProvider} from './contextApi/MainContext';
import axios from "axios";
// express server with mongodb
axios.defaults.baseURL = "http://localhost:3001";
ReactDOM.render(
<MainContextProvider>
<BrowserRouter>
<App />
</BrowserRouter>
</MainContextProvider>,
document.getElementById('root')
);
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.