Convert React Context API Class to function of Hook

*爱你&永不变心* 提交于 2021-02-10 14:19:49

问题


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.


回答1:


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




回答2:


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')
);


来源:https://stackoverflow.com/questions/64562934/convert-react-context-api-class-to-function-of-hook

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!