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

标签 javascript reactjs state-management

我刚刚开始学习 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 token 获取用户数据。

这是我的登录组件

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 token ,然后在 context.js 中调度登录操作来存储 token 以供将来使用。之后,我将用户重定向到仪表板,他可以在其中获取他生成的数据。为了获取数据,我再次使用上下文中存储的 JWT token 向后端发出 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 or later ,看起来像这样:

// 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);
  }
}

关于javascript - AJAX 调用后无法在 ReactJS 上下文 API 中设置状态,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/55764494/

相关文章:

flutter - Flutter Provider:如何通知模型包含的模型发生了更改?

javascript - 如何获取最终用户看到的 <option> 文本

javascript - AngularJS:通过字段名称获取数组值

reactjs - react <Link/> 路由器不工作。 react JS

reactjs - React formik 表单渲染速度慢的问题

python - 是否有像 Mobx for Python 这样的响应式(Reactive)状态库?

javascript - 如何使用纯 vanilla javascript 和 php 上传文件?

javascript - 为什么在使用条件(三元)运算符的 javascript 中, "="与 "==="会得到不同的结果?

javascript - 如何获取进度条div元素的点击位置

firebase - Flutter:即使在屏幕之间导航后也会保存页面的状态