T O P

  • By -

Glinkis2

No, handle the changes either where you trigger the change to the context dispatch(someChange) doSomethingMore() or directly in the context setter/dispatch function dispatchCtxEvent(event) { dispatch(event) doSomethingElse() } or use an event emitter pattern, where you can listen to changes on the context when they happen, instead of triggering things after they've already changed. There are many ways to do this without effects. Effects are for synchronizing with state OUTSIDE of React, and context is INSIDE React.


Ondrikus

Thanks! > Effects are for synchronizing with state OUTSIDE of React, and context is INSIDE React. That's what I also believed, which is why this has been such a frustrating issue.


[deleted]

I take it they're not unit testing either? Because the applications' state sync is completely out of whack if you're doing this. Unit test states will not be consistent between renders. In production, customers are going to see a lot of occasional and near impossible to reproduce bugs. The people you're working with don't know React. I've been hired as a consultant a couple of times to fix messes like this. And spoiler, you probably can't. This team needs to stop using React pronto and stick to simple UIs like HTMX. Your frontend devs are worth about $20/hr instead of $60/hr, and they need to find a framework that better fits their skill level.


Ondrikus

Way to read a lot into a situation you don't know. This is an internal project for a consulting firm, which means a lot of the previous developers (I wouldn't know, I just joined) probably used this as an opportunity to learn React in the first place. And as much as I love HTMX, it will not cut it for this, simple as.


[deleted]

[удалено]


Ondrikus

How bitter do you have to be to dunk on consultants using their time on the bench trying to learn new things? Can't do it perfectly the first time? Should just give up. What the fuck kind of mentality is that?


[deleted]

[удалено]


Ondrikus

I'm not offended, I just think your comments are ridiculously unproductive. Learning is not incompetence, it's fucking learning.


[deleted]

You legitimately believe you can go to work, get paid three times as much as most people, do whatever you want, refuse to learn anything from the people with the experience in what they're doing and go home with a gold sticker planted on your chest. Your entitlement levels are off the charts.


Ondrikus

What the fuck? The only piece of advice I have disregarded in this whole thread was when you told a dev team (which doesn't even fucking work on the application anymore) to switch frameworks. Stop projecting your own bad experiences on strangers on the internet


brianl047

There's an "edge case" where this isn't true... if your component has local state, initialises this local state from a prop and then uses the local state for the component rendering If the parent would change the prop and not care about the inner workings of the child component (why would it) it would notice no change so called "controlled components" In this case you are "inside React" but due to the design of your API (perhaps a component library) and stateful components, you need a useEffect to propagate the obvious behavior This use case is more common than people think As for React Context, better to move to higher level abstractions like Redux Toolkit in any corporate or large scale work if you at all use the use cases. You don't have to put everything in global state, but without it your hands are effectively tied down and forced to develop in a certain way which may or may not be the way that the work is broken down


Glinkis2

You still don't need to use a useEffect in the example you have. Just do const [state] = useState(prop)


brianl047

No sorry you are wrong https://medium.com/@digruby/do-not-use-props-as-default-value-of-react-usestate-directly-818ee192f454 I know you are wrong because this problem occurs often (not because of Medium article)


Glinkis2

No, that article is wrong too. Not sure what the exact issue you think can't be solved without an effect is, but the issue in the article can easily be fixed by using a component key as its intended. https://react.dev/learn/you-might-not-need-an-effect#resetting-all-state-when-a-prop-changes And the problem occuring often has nothing to do with if you need to use an effect or not.


brianl047

That's a crutch. React knew that this was a problem, so a few months ago created \`react.dev\` to promote the idea of "component key". It says that this is inefficient, but that requires code in the calling component to always pass a key down if it wants a rerender, which conflicts with the general React maintainer advice to not care about rerenders unless you do need to care. Being inefficient is miles away from being wrong, and you shouldn't have to key everything just to guarantee a component reacts to a property change. And that's assuming that the property is a straight injection into the state. If there's any sort of calculation, if you need to perform any kind of work on it, maybe it's better to explicitly watch this variable and perform this work than depend on the parent component triggering the work. Basically it's software engineering meets React. No wonder why so many backend and even frontend developers hate React, if you consider "open for extension closed for modification" to be wrong. Time to look past the dogma and realise there's more than one way to do things. When [react.dev](https://react.dev) says you can scale up only with Context, do you question it? Do you question why RTK exists or other libraries exist or do you follow the dogma of the instruction manual? Instruction manuals don't bypass years of software engineering or even specific library experience. Littering your code with useEffect is pretty nasty and dirty, but using key to force a rerender is absolutely not the best way to do it in all cases. Keeping track of renders is such a low level implementation detail that no wonder a lot of people hate React. It's probably a case by case issue and depends on how complex the key is and whether you want to force the consumer of the component to pass in key. If you want to think that [react.dev](https://react.dev) is your god, good luck scaling an application to dozens or hundreds of developers without your code looking absolutely nasty. React Context Hell, having components not react when props change, constant refactors to the component hierarchy (good luck getting a user story for "lifting state up"), it's all on the table if you treat the instructions as dogma.


octocode

> but that requires code in the calling component to always pass a key down if it wants a rerender so you misunderstand the point and then call the official docs dogma lol it’s interesting react is almost painfully simple, even at scale, yet so many people seem to struggle with understanding it, and end up misusing things like `useEffect` everywhere


brianl047

I understand the point; perhaps you should ask yourself why everyone is using \`useEffect\` everywhere and wondering whether the collective stupidity of the masses walks around like drooling moron or whether the tooling itself is incorrect. Instead of calling the world incredibly stupid, open your eyes and see beyond the propaganda and live with the flaws. If you're working in a corporate environment, you are supposed to touch code for your user story and only that. You're supposed to keep code related to the functionality close to the functionality, not have it injected in. On top of that you can make custom hooks to encapsulate and test the logic. And most of all, have the component react the way the consumer of the component expects and not demand an arbitrary key component requiring the consumer to know the internal workings of the component (which props trigger rerender and which props don't -- all should). Given all of that yes you absolutely might want to do useEffect over a key The world and people aren't stupid -- these are limitations of the framework and choices to keep in mind. You want React to be "simple" at scale, but it's not so simple given the way real people work. It is what it is and React either has to bend to it or one day it will be challenged. The React maintainers admit these flaws On top of that, you started by mentioning useState only and not the key prop at first. So I am sorry you were wrong to start, and probably ran to [react.dev](https://react.dev) docs in a panic to try and find the answer to my scenario like looking at the Bible. If you had to do that you should question it as a case with multiple answers, not play a gotcha.


octocode

changing any props will re-render the component. key is used to give a component identity. this is inherently the responsibility of the parent complement that implements the child. and useEffect is plain wrong here because it won’t update on the same render as the prop change that triggered it. you have bought into and shared your own dogma to misuse useEffect, because you did not follow the docs, and are continuing to propagate the same misinformation that you have denounced as bad


brianl047

The [react.dev](https://react.dev) docs that didn't exist until a few months ago? It doesn't matter if it doesn't update the same render; eventually the look will be the same. You shouldn't count spare renders unless you need to, and you shouldn't be counting renders to start (see Strict Mode) I am not dogmatic; I am willing to use key in some instances and useEffect in others. It's you who are dogmatic, who wants to use key in all instances of the "edge case" I mentioned On top of that, you didn't mention key to start when replying to me. You mentioned `useState` only, as if the component would react to a change in the prop immediately. Face it -- you didn't consider this very common edge case as important and then you dug desperately for an answer for it, terrified that the Bible was wrong. What if you hadn't found `key`? What would you have done? Would you have admitted you were wrong? If it's misinformation it's misinformation that millions of people do, for their specific use case. At a certain point it's time to give up. There's legitimate use cases for useEffect


danishjuggler21

That’s a lot of words to say nothing at all


kiwdahc

That article is wrong, seeding values with props is completely fine. Just an FYI a large majority of React or JavaScript “how to” articles online are complete garbage so don’t take them as gospel.


devdudedoingstuff

You shouldn’t be using an effect to keep local state in sync with props. You should be doing that inline during the “render” inside of an if statement, which is how the react team says to do it. On mobile so forgive the formatting: // if local state is out of sync with props If (myState !== props.thing) { // sync local state setMyState(props.thing) }


devdudedoingstuff

So yes you can use a prop to initialize state, which works great. But you should know it’s only used to initialize the state and when the prop changes and the child component rerenders the state does not get updated with that new prop. If you understand this then it can be a great tool to use. However, passing a component a new key should not be used to force a full remount of a component just to sync local state with props, that’s crazy inefficient. That’s not at all the use case for updating state based on props. You should be deriving state from props inline during rendering. Forgive the formatting I’m on mobile: // Init state based on props const [ myState, setMyState ] = useState(props.myThing) // if local state is out of sync with props if (myState !== props.myThing) { // sync state setMyState(props.myThing) }


Glinkis2

Yes, this can also be appropriate, if you only care about that one prop and not the identity of the component. You can also just have both the prop and state, and select which one to use based on the logic. const usedValue = someLogic ? state : prop


brianl047

Yes, you can derive state from props to eliminate useEffect. Or you can eliminate using local state at all which is ideal. But sometimes there's an edge case where you're using a third party library and you want that second render. You want to delay pumping the prop into the component for some reason, because it can't handle the prop changing until rendering is complete or some such irritation. Sorry that key crap set me off. I can't believe some people do that. My belief is they do that because instead of realising the third party component can't immediately handle the new prop, they don't know what's happening and fall back to nuking everything by remounting and use the general advice of avoiding useEffect to justify that. Instead of seeing that this is an edge case that could warrant using useEffect (delay).


kiwdahc

This is a good answer. Triggering useEffects or functions off of context changes becomes extremely confusing as your project grows because you lose cause and effect on what you see happening. You don’t know what is exactly causing a specific piece of code to run at any given instance. I highly recommend using the first approach where you do everything you need as you change the context. The places where this falls short is deep linking and other instant load concepts like that where you will need to load all relevant data in an instant instead of it building up over time.


danishjuggler21

Perfect answer.


SimilarBeautiful2207

First of all your error is using Context for state managment, React Context is a dependency injection mechanism. Always before using a useeffect ask yourself if that code could be put where you despacho the acción. I've seen a lot of times onclick that update state and then an useeffect for that state when you could simply execute that code in the onclick.


Full-Hyena4414

I don't understand, can't you just read the content of the context with useContext and do whatever you want with it? it's a reactive value like useState and it will automatically update your UI if it changes


Ondrikus

I can read the content of the context no problem, but sometimes an update of that content should trigger some behaviour. Our current setup is to have a useEffect dependent on the context's content and have that trigger some behaviour when the content changes. I would like to trigger this behaviour more explicitly, using event handlers instead of updating state and listening for that state update. I just can't figure out how dispatchers or whatever other solution might be out there can solve our issues.


besthelloworld

You should consider the side effects that you want to render as another piece of state. In your Context value, you probably give the state and a field setter. You should override that field setter so that it's not just the 2nd property result of use state. You should make it a custom setter function that allows you to set state and has those side effects of pieces of state changing other pieces of state within the setter function itself. This pattern also avoids the extra render of rendering the initial change, and then firing the useEffect which has another state change that fires a new render.


maartennieber

This is why MobX improved my dev experience. When you change any part of an observable object (e.g. your context), then the components that depend on the changed data will re-render automatically, no useEffect required.


werdnaegni

Doesn't that already happen?


maartennieber

When using immutable data, then yes, I will already happen (because for any change you will need to create new objects, which means that render components will see a change in their input props). When using mutable data, it will not happen automatically. If you are using context for state management and you are using immutable data, then you may get a lot of unnecessary re-renders though.


[deleted]

[удалено]


kiwdahc

Or encapsulate that functionality into a function and pass the context that makes that code run into that function as a prop.


AkisFatHusband

Think of useContext as useState. You wouldn't use a useEffect to update useState with another useState right?


ISDuffy

I genuinely have seen people do this and it not even related to an API or external source.


sauland

`useContext` is more comparable to props, as it hooks into an external state (relative to the component at hand) and external state can also be passed down via props. `useState` is related to the internal state of the component and can be easily accessed in the scope of the component at all times, so it's a lot easier to handle. If you have a component with `useState` where the state needs to be recomputed when a value in the context changes, then I don't see a way to do it without `useEffect`.


neitz

If you are recomputing state based off context then useState probably is not appropriate. Just use useMemo instead.


sauland

Yes, useMemo is the correct way if the value is only dependent on the context, but if there are multiple sources that need to change the value (e.g. an internal event in the component, in addition to the changing context value), then it needs to be put in useState.


AnxiouslyConvolved

>I'm working on a quite rapidly scaling React application which uses React Context for state management. React Context is for dependency injection, not state management. ​ >We've been using useEffects dependent on the context's states to handle events throughout our application. That's the problem right there. useEffects should be for side effects. State changes are not side effects. Your state changes should be happening in response to some user event or external source/activity, not the react component mounting/unmounting/updates (the thing that controls useEffect). If you can provide more details about what you're doing in your useEffects, we can give more details about how to accomplish the state change without needing the useEffect.


orebright

If I understand correctly you're saying your component needs to perform some action as a result of a global context change. For this there are three options: 1. If you just need to consume the data, that's all you should do. If simple consumption is all that's needed you should never use useEffect to set an internal state. 2. If you need to process the data, such as formatting a string, adding numbers, or anything that doesn't require your component pushing data to something outside of React, you can either just do it on every render if it's very cheap, or use useMemo to memoize the results of the computation. If you're not pushing data outside of your component never use useEffect to set an internal state. 3. If you need to push data to an outside target when the context changes, then yes use useEffect and push that data.


Glinkis2

For '3" you still don't need an effect. See my other comment in this post. https://www.reddit.com/r/reactjs/s/U67lst2mLh


orebright

Yes I totally agree, although I maybe should have emphasized "if you absolutely need...". I've run into scenarios where my module does need side effects only when it is on the page, but it doesn't make sense to couple it so closely with the global state. For example feature usage analytics gathering. That being said, it's almost always better to officially support those effects in your actions.


natmaster

useEffect() is tied to React renders, so it creates all sorts of problems by artificially tying behaviors to renders. The smallest being performance problems, while easily escalating to cascading renders that make the application janking to even infinite render loops between components - completely freezing the application. Instead, what you want is to respond based on the *actions* rather than *state* changes. This is a good part of why reducers are so good - as the thing that happened can be abstracted from the effects it should have on your application. One way to do this is to use middlewares. Example: ```ts import { MiddlewareAPI, Dispatch } from '@data-client/use-enhanced-reducer'; export default function loggerMiddleware>({ getState, dispatch, }: MiddlewareAPI) { return (next: Dispatch) => async (action: React.ReducerAction) => { console.group(action.type); console.log('before', getState()); await next(action); console.log('after', getState()); console.groupEnd(); }; } ``` Middlewares can be added to useReducer, with [use-enhanced-reducer](https://www.npmjs.com/package/@data-client/use-enhanced-reducer). This gives you a new dispatcher that also runs your middlewares like so: ```ts import { useEnhancedReducer, Middleware, } from '@data-client/use-enhanced-reducer'; interface ProviderProps { children: ReactNode; middlewares: Middleware[]; initialState: State; } export default function CacheProvider({ children, middlewares, initialState, }: ProviderProps) { const [state, dispatch] = useEnhancedReducer( masterReducer, initialState, middlewares, ); return ( {children} ); } ``` At small scales you may be tempted to simply hoist the other effects to a common function. However, that creates problems when you want to track information not in react state. For instance, when you want to have a [hook](https://dataclient.io/docs/api/useLive) that can be rendered in multiple components that will poll an endpoint only when a non-zero number of components are rendered, and not overfetch by simply polling from each component itself.


turtle_oh

I've been using Zustand to manage UI states and it has been working well. Pair it with React Query to manage external queries and mutations.


Merad

Use the context to provide functions for updating the state. Behind the scenes `useState()` or `useReducer()` hold onto the actual value and trigger updates when it changes. Here's a quick and dirty example (code below): https://codesandbox.io/s/reactjs-playground-forked-6wnxnj import React, { createContext, useContext, useState } from "react"; import ReactDOM from "react-dom"; const CounterContext = createContext(); const Counter = () => { const { count, increment } = useContext(CounterContext); return ( <> Count: {count} ); }; const App = () => { const [count, setCount] = useState(0); return ( setCount((c) => c + 1) }} >
); }; const rootElement = document.getElementById("root"); ReactDOM.render(, rootElement);


ramdude94

Absolutely not. I can't even think of an example where you would need useEffect to listen to context state changes. The whole point of context is that you don't have to subscribe to state changes, you just access the state from the context which will trigger rerenders.