3
\$\begingroup\$

This is a pretty simple working example using React-Redux to select a user from app.com/user/:userId and then fetch/display that user's info from a remote API

Most of the examples/code I can find online use class-based components but since I already have part of a project written using a functional approach I'm looking for feedback on how I'm integrating redux into functional components. I'm also not a Javascript expert by any means either so general critique of my JS is welcome as well!

Store.js:

import { createStore } from "redux";

const initialUser = { user: [] };

const userReducer = (state = initialUser, action) => {
  switch (action.type) {
    case "ADD_USER":
      return Object.assign({}, state, { user: action.user });
    default:
      return state;
  }
};

export default function Store() {
  return createStore(userReducer);
}

Actions.js:

const validResp = (resp) => {
  return resp.data.code === 200;
};

const extractUser = (resp) => {
  return resp.data.data;
};

export const addUser = (resp) => {
  let user = "";

  if (validResp(resp)) {
    user = extractUser(resp);
  }

  return {
    type: "ADD_USER",
    user: user
  };
};

index.js:

import React from "react";
import ReactDOM from "react-dom";
import { Provider } from "react-redux";
import { BrowserRouter as Router, Route } from "react-router-dom";

import AppContainer from "./AppContainer";
import Store from "./Store";

const rootElement = document.getElementById("root");
ReactDOM.render(
  <Provider store={Store()}>
    <Router>
      <Route path="/user/:userId" component={AppContainer} />
    </Router>
  </Provider>,
  rootElement
);

AppContainer.js:

import React from "react";
import { useEffect } from "react";
import { useDispatch, useSelector } from "react-redux";
import { useParams } from "react-router-dom";
import axios from "axios";

import { addUser } from "./Actions";

const baseUrl = "https://gorest.co.in/public-api/users/";

export default function AppContainer() {
  const url = baseUrl + useParams().userId;
  const user = useSelector((state) => state.user);
  const dispatch = useDispatch();

  useEffect(() => {
    axios.get(url).then((resp) => dispatch(addUser(resp)));
  });

  return <App user={user} />;
}

function App(props) {
  return (
    <div className="App">
      Name: {props.user.name} <br />
      id: {props.user.id}
    </div>
  );
}
\$\endgroup\$
1
\$\begingroup\$

Concisely merging objects can be done with spread syntax instead of Object.assign. The ADD_USER case can use:

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

Using spread syntax instead of Object.assign doesn't have a big impact here, but it makes things so much easier when the state structure grows to be large and complicated.

Functional programming does not reassign variables - everything should be declared with const. If you want to be more functional, change addUser to use the conditional operator to either assign the empty string or the extractUser result to the user variable:

export const addUser = (resp) => {
  const user = validResp(resp) ? extractUser(resp) : "";
  return {
    type: "ADD_USER",
    user
  };
};

(Note that if the key is named the same as the variable the value is in, you can use shorthand property names like above. You could also return the whole object at once without declaring a user variable first.)

useEffect bug Since you didn't provide a dependency array to useEffect, its callback will run on every render. I'm pretty sure this isn't desirable - you want it to run only on the initial render, right? Pass an empty dependency array to useEffect so that the callback inside only runs on mount:

useEffect(() => {
  axios.get(url).then((resp) => dispatch(addUser(resp)));
}, []);

Error handling If the axios.get Promise rejects, no indication is given. Always .catch Promises somewhere. You might want something like

useEffect(() => {
  axios.get(url)
    .then((resp) => dispatch(addUser(resp)))
    .catch((error) => dispatch(addError(error.message)));
}, []);

or something of the sort, to show that an error occurred.

\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.