React Rails身份验证-无需重新加载页面即可获取用户状态

时间:2020-03-04 12:17:11

标签: ruby-on-rails reactjs

我正在建立一个在后端使用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.js转移到App.js或直接从App.js更新Login.js的状态并利用{{1} } useEffect中,并无需手动刷新浏览器即可更新状态,但是我不知道该怎么做。

1 个答案:

答案 0 :(得分:0)

通过在case上创建新的App.js并从case更新Login.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的情况可以很容易地通过useContext传递给Login.js,然后根据需要更新该情况。

// 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...
}