簡體   English   中英

AJAX調用后無法在ReactJS上下文API中設置狀態

[英]Not able to set the state in the ReactJS context API after AJAX call

我剛開始學習ReactJS,所以我決定在ReactJS中使用新的上下文API來管理我正在學習的項目中的狀態。

這是context.js代碼,

import React, { Component } from "react";
import axios from "axios";

const Context = React.createContext();

const reducer = async (state, action) => {
  switch (action.type) {
    case "USER_LOGIN":
      const { token } = action.payload;

      return { ...state, user: { token } };

    case "GET_USER_DATA":
      const url = "api/users/dashboard";
      const userToken = action.payload.token;

      let res = await axios.get(url, {
          headers: {
            Authorization: userToken
          }
      })


      let urls = res.data.urls;
      urls = urls.map(url => ( { ...url,shortUrl: axios.defaults.baseURL + "/" + url.urlCode} ) )

      return { ...state, user: { token } };

  }
};

export class Provider extends Component {
  state = {
    user: {
      token: "",
      data: [{id: 'adsasd'}]
    },
    dispatch: action => {
      this.setState(state => reducer(state, action));
    }
  };


  render() {
    return (
      <Context.Provider value={this.state}>
        {this.props.children}
      </Context.Provider>
    );
  }
}

export const Consumer = Context.Consumer;

我有兩種類型的操作,一種是登錄,另一種是根據成功登錄后收到的JWT令牌獲取用戶數據。

這是我的登錄組件

import React, { Component } from "react";
import { Row, Col, Input, Icon, CardPanel, Button } from "react-materialize";
import axios from 'axios'
import { Consumer } from '../store/context'

class Login extends Component {
  state = {
    errors: {
      name: "",
      password: ""
    }
  };

  constructor(props) {
    super(props);
    this.emailInputRef = React.createRef();
    this.passwordInputRef = React.createRef();
  }


  login = async (dispatch) => {
    const email = this.emailInputRef.state.value;
    const password = this.passwordInputRef.state.value;

    if (typeof password != "undefined" && password.length < 6) {
      this.setState({ errors: { password: "Password length must be atleast 6 characters!" } })
    }
    else {
      this.setState({ errors: { password: "" } })
    }

    if (typeof email != "undefined") {
      if (!validateEmail(email)) {
        console.log('invalid email');

        this.setState({ errors: { email: "Invalid email address!" } })
      }
      else {
        this.setState({ errors: { email: "" } })
      }
    }
    else {
      this.setState({ errors: { email: "Invalid email address!" } })
    }

    // console.log(this.state.errors);

    if ((email !== "" || typeof email !== "undefined") && (password !== "" || typeof password !== "undefined")) {

      const res = await axios.post('/api/users/login', {
        'email': email,
        'password': password
      })


      dispatch({
        type: 'USER_LOGIN',
        payload: {
          token: res.data.data.token
        }
      })

      this.props.history.push('/dashboard')


    }

  }

  render() {
    const { errors } = this.state;
    return (
      <Consumer>
        {value => {

          const { dispatch } = value

          return (
            <CardPanel className="bg-primary" style={{ padding: "20px 5%" }}>
              <Row className="login">
                <h1 style={{ color: "white" }}>Login</h1>
                <Col s={12} m={12}>
                  <Input
                    s={12}
                    m={12}
                    name="email"
                    error={errors.email}
                    className="error"
                    label="Email"

                    ref={ref => this.emailInputRef = ref}
                  >
                    <Icon>account_circle</Icon>
                  </Input>

                  <Input
                    s={12}
                    m={12}
                    name="password"
                    error={errors.password}
                    label="Password"

                    type="password"
                    ref={ref => this.passwordInputRef = ref}
                  >
                    <Icon>lock</Icon>
                  </Input>
                  <Button onClick={this.login.bind(this, dispatch)} style={{ marginTop: "20px" }} waves="yellow">
                    Login
                </Button>
                </Col>
              </Row>
            </CardPanel>
          )
        }}

      </Consumer>

    );
  }
}

function validateEmail(sEmail) {
  const reEmail = /^(?:[\w\!\#\$\%\&\'\*\+\-\/\=\?\^\`\{\|\}\~]+\.)*[\w\!\#\$\%\&\'\*\+\-\/\=\?\^\`\{\|\}\~]+@(?:(?:(?:[a-zA-Z0-9](?:[a-zA-Z0-9\-](?!\.)){0,61}[a-zA-Z0-9]?\.)+[a-zA-Z0-9](?:[a-zA-Z0-9\-](?!$)){0,61}[a-zA-Z0-9]?)|(?:\[(?:(?:[01]?\d{1,2}|2[0-4]\d|25[0-5])\.){3}(?:[01]?\d{1,2}|2[0-4]\d|25[0-5])\]))$/

  if (sEmail === "") return false;

  return reEmail.test(sEmail);
}

function isEmpty(obj) {
  if (obj == null) return true;
  return Object.entries(obj).length === 0 && obj.constructor === Object;
}

export default Login;

我想要實現的是,當用戶嘗試登錄時,我向后端發出請求並接收JWT令牌,然后在context.js中調度一個登錄操作以存儲該令牌以備將來使用。 之后,我將用戶重定向到儀表板,在那里他可以獲取他生成的數據。為了獲取數據,我再次向后端發出AJAX請求,並在上下文中存儲了JWT令牌。 我在componentDidMount()方法中執行此操作,但是當我嘗試訪問上下文數據時,總是收到空對象。 這是儀表板

Dashboard.jsx

   import React, { Component } from 'react'
import axios from 'axios'
import 'react-bootstrap-table-next/dist/react-bootstrap-table2.min.css';
import BootstrapTable from 'react-bootstrap-table-next';
import overlayFactory from 'react-bootstrap-table2-overlay';

import { Consumer } from '../store/context'

const columns = [
    {
        dataField: 'url',
        text: 'URLs'
    },

    {
        dataField: 'hits',
        text: 'Hits'
    },
    {
        dataField: 'shortUrl',
        text: 'Short URL'
    },
    {
        dataField: 'createdDate',
        text: 'Date'
    },
];

export default class Dashboard extends Component {

    state = {
        data: []
    }

    componentDidMount() {

        // const url = 'api/users/dashboard'

        const context = this.context

        console.log(context); // always empty

    }

    render() {
        return (
            <Consumer>
                {value => {
                    const { user } = value

                    return (
                        isEmpty(user) ? <h3 className="center-align">Please Login To View Dashboard...</h3> : (
                            < BootstrapTable keyField='shortUrl'
                                data={this.state.data}
                                columns={columns}
                                bordered={true}
                                hover={true}
                            />
                        )
                    )

                }}
            </Consumer>
        )
    }
}

function isEmpty(obj) {
    if (obj == null) return true;
    return Object.entries(obj).length === 0 && obj.constructor === Object;
}

默認情況下, this.context是未定義的。 為了填充它,您需要告訴react用它填充什么。 假設您使用的是React 16.6或更高版本 ,則如下所示:

// In context.js, you must export the entire context, not just the consumer
export const Context = React.createContext();

// In Dashboard.jsx, you must import the context, and add a static contextType property to your component

import { Context } from '../store/context';

export default class Dashboard extends Component {
  static contextType = Context;

  componentDidMount() {
    console.log(this.context);
  }
}

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM