DEV Community

Cover image for React... Why so complicated...?
Joel Ruiz
Joel Ruiz

Posted on • Edited on

React... Why so complicated...?

React has really great concepts. But when it comes to data management, everyone keeps coming up with more ridiculous methodologies and frameworks with attempts to create syntactic artwork.

I'll say it right now.

It's unreadable and overly complicated, more than it needs to be.

Oh, you think differently?

Let's start with the popular Redux for React, with the most basic example.

export const setVisibilityFilter = filter => ({
  type: 'SET_VISIBILITY_FILTER',
  filter
})
//...
const visibilityFilter = (state = VisibilityFilters.SHOW_ALL, action) => {
  switch (action.type) {
    case 'SET_VISIBILITY_FILTER':
      return action.filter
    default:
      return state
  }
}
Enter fullscreen mode Exit fullscreen mode

2 files, 12 lines of code, one purpose, set the visibility filter value. And it is still incomplete! We have to add the reducer to the store, import the actions wherever we want to use them, all the while VSCode is just asking, huh? what?

But you might say, it's about having a predictable state container. Well, once you add thunks and start mixing state values, predictability flies out the window.

In addition, these reducers are simple, but in real-world applications, they are never as simple. They grow large, so you start breaking them up into functions, which don't fit nicely in the same file, so you create more files. Now you are bouncing around all these files just to manage one state of data.


Let's jump into Reacts version of redux, oh boy, settle in.

const initialState = {count: 0};

function reducer(state, action) {
  switch (action.type) {
    case 'increment':
      return {count: state.count + 1};
    case 'decrement':
      return {count: state.count - 1};
    default:
      throw new Error();
  }
}

function Counter() {
  const [state, dispatch] = useReducer(reducer, initialState);
  return (
    <>
      Count: {state.count}
      <button onClick={() => dispatch({type: 'decrement'})}>-</button>
      <button onClick={() => dispatch({type: 'increment'})}>+</button>
    </>
  );
}
Enter fullscreen mode Exit fullscreen mode

Alright, a full example just for you. How many seconds did it take you to follow the code and all its purpose? You rockstars would probably say about 3-5 seconds. Well duh, you bathe in this all day.

Take a look at useReducer. This provides all the technology for mutating the state of your component. What would happen to the code if we need to use, say, 2 or 3 different states. Now you've introduced some serious ugliness...

const [state1, dispatch1] = useReducer(reducer1, initialState1);
const [state2, dispatch2] = useReducer(reducer2, initialState2);
const [state3, dispatch3] = useReducer(reducer3, initialState3);
Enter fullscreen mode Exit fullscreen mode

You better not use that naming.

Does anyone even useReducer? This becomes a formatting nightmare to manage all the reducers, just with this example using 12 different named variables. The amount of naming you have to do will just grow larger the more code integration you attempt to perform.


The next ridiculous is with React's Context...

const ThemeContext = React.createContext('light');
class App extends React.Component {
  render() {
    return (
      <ThemeContext.Provider value="dark">
        <Toolbar />
      </ThemeContext.Provider>
    );
  }
}
Enter fullscreen mode Exit fullscreen mode

It's more readable. But, we are forcing data to have a relationship with a specific component in a parent/child fashion. This is not ideal in real-world, where business requirements change frequently, and you end up having to heavily refactor to fit in some weird edge case.

class ThemedButton extends React.Component {
  static contextType = ThemeContext;
  render() {
    return <Button theme={this.context} />;
  }
}
Enter fullscreen mode Exit fullscreen mode

Why would you do this to yourself. You basically created a global variable that has to be individually referenced for each type of context! What if you need 10 different context categories. Let me play my violin for you while you figure out how to best format it, for the next few days.

Let's move on to MobX...

class ObservableTodoStore {
    @observable todos = [];
    @observable pendingRequests = 0;

    constructor() {
        mobx.autorun(() => console.log(this.report));
    }

    @computed get completedTodosCount() {
        return this.todos.filter(
            todo => todo.completed === true
        ).length;
    }
}
const observableTodoStore = new ObservableTodoStore();
Enter fullscreen mode Exit fullscreen mode

Annotations, annotations, annotations. These are eyeball magnets in any language, but some people love them, so they get a pass for now. At least we are starting to get back on track with the time tested Services-oriented programming.

@observer
class TodoList extends React.Component {
  render() {
    const store = this.props.store;
    return (
      <div>
        { store.report }
        <ul>
        { store.todos.map(
          (todo, idx) => <TodoView todo={ todo } key={ idx } />
        ) }
        </ul>
        { store.pendingRequests > 0 ? <marquee>Loading...</marquee> : null }
        <button onClick={ this.onNewTodo }>New Todo</button>
      </div>
    );
  }

  onNewTodo = () => {
    this.props.store.addTodo(prompt('Enter a new todo:','coffee plz'));
  }
}

ReactDOM.render(
  <TodoList store={ observableTodoStore } />,
  document.getElementById('reactjs-app')
);
Enter fullscreen mode Exit fullscreen mode

This seems a bit cleaner right. Except, now you have to manage passing your store and its data down the hierarchy again like the Context example above. This went backwards pretty fast. This is the reason Redux came out, to avoid having to trickle down your data manually.

That being said, I do enjoy the Services nature to having direct access to the methods and data with no exotic formating.


Can all of this be done better? Maybe... I wasted my weekend prototyping my ideal setup, but this is not a problem that can be easily solved by a single person.

Here is an example of what I mashed together...

//Run a query against DuckDuckGo API
export async function SearchDuckDuckGo(query) {
    let url = 'https://api.duckduckgo.com/?t=flatstoreExample&format=json&q=' + query;
    try {
        let response = await axios.get(url);
        let results = ReduceResults(response); //grabs only the results

        flatstore.set("ddg", response.data);
        flatstore.set("ddgQuery", query);
        flatstore.set("ddgResults", results);
        flatstore.set("ddgResultCount", results.length);
        flatstore.set("ddgError", false);
    }
    catch (error) {
        console.log(error);
        flatstore.set("ddgError", error);
    }
}
Enter fullscreen mode Exit fullscreen mode

Focus is on readability and usability. A simple action to Search DuckDuckGo. It does its work, then saves the data in key/value format.

Ok, great, you the man, now what about showing it? Well, I played my violin over the weekend thinking about it, and came up with something like this...

class SearchStatus extends React.Component {
    render() {
        if (this.props.ddgError)
            return (
                <div style={{ color: '#f00' }}>
                    {this.props.ddgError.message}
                </div>
            );

        return (
            <div>
                <i>
                    Searched {this.props.ddgQuery}
                    with {this.props.ddgResultCount || 0} results.
                </i>
            </div>
        );
    }
}

export default flatstore.connect(['ddgQuery', 'ddgResultCount', 'ddgError'])(SearchStatus);
Enter fullscreen mode Exit fullscreen mode

Redux was brilliant in using a higher-order component. This allows you to remove all the framework craziness away from a component, and let the magic be done in the background.

In that respect, I stole it. But, we just want specific data points, so why not allow the user to directly specify what keys we need without circlejerking yourself.

I couldn't help myself, I had to go further. Real-world applications get complicated fast with all the business requirements coming from three or four levels above you. We need dynamic control, so we are back again to getting inspiration from redux's connect prop mapping.

class TodoResult extends React.Component {
    render() {
        return (
            <div className={this.props.completed ? "completed" : ""}
                onClick={() => { todoToggleComplete(this.props.id) }}>
                <span className="result-title">{this.props.desc}</span> -
                <span className="result-date">{this.props.dateCreated}</span>
            </div >
        );
    }
}

let onCustomWatched = (ownProps) => {
    return ['todos-' + ownProps.id];
}
let onCustomProps = (key, value, store, ownProps) => {
    return {
        ...value
    }
}
export default flatstore.connect([], onCustomWatched, onCustomProps)(TodoResult);
Enter fullscreen mode Exit fullscreen mode

Except, this time we are limiting the onCustomProps to only those keys we specifically are watching. I even added object drill down, so I can watch a sub-item of the main "todos" object. React is about reacting only when needed, so I tried to only react when the components relevant data changes, with minimal coding effort for the developer.


I spend a lot of time teaching React, so most of this rant comes from what I see is confusing the new developers. There are many misunderstandings with coding in React, due to the complexity of modern JavaScript syntax used by the latest frameworks. It achieves very little, with so much code and files.

I was happy with the result of my prototype called flatstore, but its no where near usable in the real-world, so it'll be another one of my new projects that gets to ferment on GitHub.

In the meantime, I'll be wishing for one of you geniuses to bring back simplicity to programming.

Oldest comments (21)

Collapse
 
crimsonmed profile image
Médéric Burlet

You could try looking into overmindjs.org/

its pretty neat and I think the syntax and usability is much better than redux.

Collapse
 
rimlin profile image
Ilmir Shaikhutdinov

Good post! I agree that a react state management can be complicated. To make it easier, you can use an approach the smart/dumb component, where about state know only smart components.
For myself i find that the less state in the app, the more comprehensible the code will be. Is it cool when you state in one place and not spread at all app, for this case i prefer Akita. It is more convenient, OOP-like state management, you can read about this here.

Collapse
 
seanmclem profile image
Seanmclem • Edited

In the first few lines of this article you quickly equate how complicated Redux can be with how complicated react is. React in itself can be rather simple and elegant. Adding a whole separate Library called Redux doesn't make react itself more complicated. It's the library that you're adding.

React's own State Management, context api, could be a bit intimidating at first too, but less than redux. Which is why you shouldn't use any state management at all when starting to learn react. You should probably just start with simple components, passing the state through props, and slowly add more complicated things.

Also, with a modest amount of patient reading all of the code samples you showed can be understood. All it takes is a little bit of JavaScript literacy and some time.

It didn't become as popular as it is because of unreadability and needless complication. Like any other programming concept all it takes is time, patience, and capacity to learn.

Collapse
 
joetex profile image
Joel Ruiz • Edited

You make great points. I should have started with my React examples.

But the examples I have shown are not ideal for actual applications. There is a constant need to cross reference data, and it should be ready to happen at any time.

I feel like many frameworks try to have you hardcode data relationships against each component.

If we lock ourselves in, we just end up having to refactor several parts to add the new feature. For instance, NoSQL is popular for modern applications, but then we turn 180 degrees and lock down data in our applications.

If we can freely access any data at any time, it might increase chaos, but coding time for wild requirements can be handled quickly.

Collapse
 
seanmclem profile image
Seanmclem

It's just that, React is a simple UI library. How you get and manage data is really up to you. It can be whatever you want it to be.

Collapse
 
cullophid profile image
Andreas Møller
const Counter () => {
  const [count, setCount] = useState(0);
  return (<button onClick={() => setCount(count + 1)}>{count}</button>)
}

... You are welcome

Collapse
 
joetex profile image
Joel Ruiz • Edited

This just in, a new business requirement! Add users name entered from another component. And based on their role, they increment by 2.

Your setup is cleaner though!

Collapse
 
cullophid profile image
Andreas Møller

Add the user state to a common ancestor. Pass the user object down as props.

State management is by far the hardest part of frontend development. But react offers some if the best tools out there.

Collapse
 
vonheikemen profile image
Heiker

What if you push (almost) all the responsability to the actions?

You could tweak redux a little bit by writing a middleware.

const thunk = store => next => action =>
    typeof action === 'function'
      ? next({type: '', state: action(store.getState)})
      : next(action)

Then create a very generic reducer.

const reducer = function(state, action) {
  if(typeof action.state !== 'undefined') {
    return Object.assign({}, state, action.state)
  }

  return initialState()
}

And now you can have actions like this:

let nextTodoId = 0
export const addTodo = text => state => {
  // complexity, my old friend, you can live here

  return {
    todos: state().todos.concat({
      id: nextTodoId++,
      text,
      completed: false
    })
  }
}

export const setVisibilityFilter = filter => state => ({
  visibilityFilter: filter
})
Collapse
 
joetex profile image
Joel Ruiz • Edited

This has lots of potential! Thank you for sharing.

With a bit more tweaks this could be very clean for developers to use without much thought. I will play with this trick thanks!

Collapse
 
maniac0s profile image
mindphuq • Edited

And there are still devs out there that claim, React is as simple as just writing some HTML with a few JS code in between.

Might be true if all you do is a todoList. But want to start React in a more complex manner? You get stuck in that state thing.

I had the "luck" to get pushed into React (which is completely new to me) with writing an app that let's the user construct designs. It uses external libs, http-calls, backend connection, single sign-on requirements ect. And all that needs to be passed around into different children and then you want to write it in a way, that you know in 3 years wtf you did back then. I still want to see someone who does that with "just write some HTML"...

Collapse
 
cesee profile image
Cesar Maroun

Tried React for a few days. It felt like all those too many developers who insist on not using a simple solution because they just cannot see it.

Collapse
 
keymannerdawid profile image
Dawid Kimana • Edited

I think react became very popular because of 2 things: the concepts it introduced, and there were no better alternatives at the time (before vue and angular 2+). I write react, and it is needlessly complicated. The pro is that you'll become a better js developer, the con is weeping and gnashing of teeth.. Wrote a small app using svelte and what a breath of fresh air! I think the svelte way of doing things is the future

Collapse
 
snowprimate profile image
SnowPrimate • Edited

That's a nice post despite being a noobie. I've searched this article just to rant on React, learning it just to learn something new but now I actually feel a bit more motivated because it seems like I'm not alone in this frustration! :D
Also, Joel, do you still stand for this, or do you feel like React developers created solutions for these issues in the last months?

Collapse
 
ohullv profile image
ohullv

Viejo, he tenido exactamente el mismo pensamiento que tu, el Frontend es un simple Client API con UI XD.

Saludos.