简体   繁体   English

将 React Context API Class 转换为 Hook 的函数

[英]Convert React Context API Class to function of Hook

How to change this class base Context API to reach Hook without changing other components that already consumed it?如何更改此类基础 Context API 以到达 Hook,而无需更改已使用它的其他组件? 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.我用这个 MainContextProvider 包装 index.js 以便所有组件都可以使用状态或使用方法。

Here is how to use context with hooks and keeping the same API as what you already have:以下是如何使用带有钩子的context并保持与现有 API 相同的 API:

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查看StackBlitz 上演示

As Sam R. suggestion, I make little modification and works as expected.作为 Sam R. 的建议,我几乎没有修改并按预期工作。 Maybe it's better to use Reducer but I prefer not.也许使用 Reducer 更好,但我不喜欢。 And I think Context API is more simple compare to Redux.而且我认为 Context API 比 Redux 更简单。

MainContext.js :主上下文.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 :登录.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 :索引.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')
);

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM