无法在 AJAX 调用后在 ReactJS 上下文 API 中设置状态



我刚刚开始学习ReactJS,我决定在ReactJS中使用新的上下文API来管理我在学习时正在构建的项目的状态。

这是上下文.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 令牌,然后在上下文中调度登录操作.js以存储令牌以供将来使用。之后,我将用户重定向到仪表板,他可以在其中获取他生成的数据,为了获取数据,我再次使用上下文中存储的 JWT 令牌向后端发出 AJAX 请求。我在 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);
  }
}

最新更新