首页 > 解决方案 > React rails authentication - 无需重新加载页面即可获取用户状态

问题描述

我正在使用后端的 Rails API 和前端的 React 构建一个 SPA。用户身份验证按需要工作,我正在从 rails 获取用户状态。问题是当用户登录应用程序时,我必须手动刷新整个浏览器才能更新状态。

// App.js

function userReducer(state, action) {
  switch (action.type) {
    case "success": {
      return {
        ...state,
        id: action.id,
        username: action.username,
        email: action.email,
        logged_in: action.status,
        error: ""
      };
    }
    case "fail": {
      return {
        ...state,
        id: "",
        username: "",
        email: "",
        logged_in: false,
        error: action.error
      };
    }
    default:
      return state;
  }
}
// set initial state of the logged in user
const intialUserState = {
  id: "",
  username: "",
  email: "",
  logged_in: false,
  error: ""
};

export const UserStatus = React.createContext(intialUserState);

function App() {
  const [userState, dispatch] = useReducer(userReducer, intialUserState);

  // fetch the user's data
  function fetchLoggedInUserData() {
    axios
      .get("http://localhost:3000/api/v1/logged_in", { withCredential: true })
      .then(response => {
        const { id, username, email, logged_in } = response.data;
        if (response.status === 200) {
          dispatch({
            type: "success",
            id: id,
            username: username,
            email: email,
            status: logged_in
          });
        }
      })
      .catch(error => {
        dispatch({ type: "fail", error: error });
      });
  }

  // when the app loads, check if the user is signed in or not.
  // if the user is signed in, then store the user's data into the state
  useEffect(() => {
    fetchLoggedInUserData();
  }, []);

  return (
    <UserStatus.Provider value={{ userState, dispatch }}>
      <Router>
        <>
          <Switch>
            <Route path="/admin" component={Admin} />
            <Route path="/" render={props => <Public {...props} />} />
          </Switch>
        </>
      </Router>
    </UserStatus.Provider>
  );
}

上面的代码按预期工作,我可以存储您从代码中看到的用户状态。问题是当用户单击提交按钮时,我想自动存储用户而无需刷新页面。下面的代码来自Login.js

// Login.js

function signinReducer(state, action) {
  switch (action.type) {
    case "field": {
      return {
        ...state,
        [action.field]: action.value
      };
    }
    case "signin": {
      return {
        ...state,
        error: "",
        isLoading: true
      };
    }
    case "success": {
      return {
        ...state,
        error: "",
        isLoading: false
      };
    }
    case "error": {
      return {
        ...state,
        isLoading: false,
        error: action.error
      };
    }
  }
}

const initialState = {
  username: "",
  password: "",
  isLoading: false,
  error: ""
};

function FormMain() {
  const [signinState, dispatch] = useReducer(signinReducer, initialState);

  const { username, password, isLoading, error } = signinState;

  const handleSubmit = async e => {
    e.preventDefault();
    dispatch({ type: "signin" });
    await postUserData();
  };

  function postUserData() {
    axios
      .post(
        "http://localhost:3000/api/v1/sessions",
        {
          user: {
            username: username,
            password: password
          }
        },
        { withCredentials: true }
      )
      .then(response => {
        if (response.status === 200) {
          dispatch({ type: "success" });
        }
      })
      .catch(error => {
        // dispatch({ type: "error", error: error.response.data[0] });
      });
  }
}

我已经从上面的代码中删除了登录表单,因为它太长了。解决方案可以以某种方式将状态从Login.jsto转移App.js或直接更新App.jsfrom the的状态Login.js并使用useEffectin App.js,并更新状态而无需手动刷新浏览器,但我不知道该怎么做。

标签: ruby-on-railsreactjs

解决方案


通过创建一个新case的 toApp.jscaseLogin.js.

// App.js

function userReducer(state, action) {
  switch (action.type) {
    case "success": {
      return {
        ...state,
        id: action.id,
        username: action.username,
        email: action.email,
        admin: action.admin,
        logged_in: action.status,
        error: ""
      };
    }
    case "fail": {
      return {
        ...state,
        id: "",
        username: "",
        email: "",
        admin: false,
        logged_in: false,
        error: action.error
      };
    }
    case "globalFetch": {
      return {
        ...state,
        id: action.id,
        username: action.username,
        email: action.email,
        admin: action.admin,
        logged_in: action.status,
        error: ""
      };
    }
    default:
      return state;
  }
}

如您所见,globalFetch可以轻松地将案例传递给Login.js通过 useContext,然后根据您的要求更新案例。

// Login.js

function postUserData() {
    axios
      .post(
        "http://localhost:3000/api/v1/sessions",
        {
          user: {
            username: username,
            password: password
          }
        },
        { withCredentials: true }
      )
      .then(response => {
        if (response.status === 200) {
          const { id, username, email, logged_in, admin } = response.data;
          dispatch({ type: "success" });
          state.dispatch({
            type: "globalFetch",
            id: id,
            username: username,
            email: email,
            admin: admin,
            status: logged_in
          });
        }
      })
      .catch(error => {
        // dispatch({ type: "error", error: error.response.data[0] });
      });
  }
 // More code...
}

推荐阅读