React: Changing Context in Consumer - javascript

The below question relates to the following sections in the React Context documentation:
Dynamic Context
Updating Context from a Nested Component
Disclaimer: Apologies for all the background information below. It provides context and will hopefully be helpful to future visitors.
What We Know
Link 1
The (default) context value is set to themes.dark (an object that contains two properties: foreground and background)
The default value is only ever used if there are no Providers above the Consumer in the component tree
In this case, there is a Provider present in the top-level component (App)
This Provider (App), passes down its own state as the context value
It is smart to keep the values provided by a Provider equal in structure and type to the default context value (avoids Consumers getting confused)
Thus, state in the top-level component (App) holds an object of the same format as the default context value: themes.light
Conclusion from the above: When a Consumer reads the context, it reads App's state
In other words, we are here using context to pass a parent (App) state deep down in the component tree, without having to pass it through every component in the middle
When state in the top-level component (App) changes, it re-renders and a new value for state is provided to the Consumer
This way, the Consumer reads the parent's state, via context
...
Moving on, we see in link 1 that a function to set state (toggleTheme) is passed down the component tree as a normal prop
Thus, in link 1, context only contains an object that reads state
We are able to set state in the Consumer by passing the setState function as a normal prop from the Provider's child, down through all the intermediate components, and in to the Consumer
Setting the state in the top-level component (App), leads to a re-render of itself, which leads to a re-render of the Provider, which then passes the new App state value down to its Consumer via context
As such, the Consumer always knows App's state, via context
In conclusion, the flow is:
Parent's state is provided as context value to child Consumer(s)
Parent's state is updated by some child
Parent re-renders
Provider sees that context value (App's state) has changed, and re-renders all its Consumers with the new value
Link 2
In link 2, we set state in the Consumer, by passing the setState function within the context
This differs from link 1, where we relied on a normal prop to set state
Questions
We know from the docs that:
Every Context object comes with a Provider React component that allows
consuming components to subscribe to context changes....
All consumers that are descendants of a Provider will re-render
whenever the Provider’s value prop changes.
Let's assume we use a normal variable in App as the context value. We know from the above quote that changing it leads to the Provider re-rendering. Why then, do we bother using state as the context value? What is the benefit of that, vs. just using any normal variable in App?
Both the two approaches above allow us to update state. Why is link 2 incorporating the function to update state within state itself? Could we not just have it as a separate setState function, which is passed to the Consumer via context in an object that has two properties (one is state and the other is the standalone function to update state)?

Let's assume we use a normal variable in App as the context value. We know from the above quote that changing it leads to the Provider re-rendering. Why then, do we bother using state as the context value? What is the benefit of that, vs. just using any normal variable in App?
It's true that when the provider is rerendered with a changed value, any descendents that care about the context will rerender. But you need something to cause the provider to rerender in the first place. This will happen when App's state or its props change (or when you call forceUpdate, but don't do that). Presumably, this is at the top of your application, so there are no props coming in, which means you'll use state to cause it to rerender.
Both the two approaches above allow us to update state. Why is link 2 incorporating the function to update state within state itself? Could we not just have it as a separate setState function, which is passed to the Consumer via context in an object that has two properties (one is state and the other is the standalone function to update state)?
When deciding whether to rerender descendants due to a change of context, react will do basically a === between the old value and the new value. This is super quick and works well with React's preference for immutable data, but when using objects as your value you need to be careful that you're not making new objects on every render. For example, if App is doing something like the following, it will be creating a brand new object every time it renders, and thus will be forcing all the context consumers to rerender as well:
class App extends Component {
state = {
data: {
hello: 'world',
}
}
updateData() {
// some function for updating the state
}
render() {
return (
<MyContext.Provider value={{
data: this.state.data,
updateData: this.updateData
}} />
)
}
}
So the example where they store the function in state is to make sure that the entire value they're providing does not change from one render to another.

Let's assume we use a normal variable in App as the context value. We know from the above quote that changing it leads to the Provider re-rendering. Why then, do we bother using state as the context value? What is the benefit of that, vs. just using any normal variable in App?
When you use state and update it - it does not matter at all if you are using provider or not - all the provider and components under it will update. Thats under official React Context documentation and is wrong. It means changing provider values DOES NOT call consumer updates at all.
You can validate this by making a separate component with state (which would not be inside provider) and assign that state variable to the provider. So when component state changes, value in state changes and in turn provider should notice this and update consumer. Which it is NOT doing.
In order to update components under consumers, you, unfortunately, have to do it manually. Unless your intension is to update everything under the provider.
This is true as of 2021-04-21 under React 17.0.2 - provider value changes are not being monitored and consumers are not being updated sadly. Unless you put all your provider in component with state, but changing its state forces updating all components under the provider. Sadly.

Related

Can state be named differently (with another name like 'globalState') in ReactJS?

I am new to React (just 1 online course) and am discovering the magic of props and state. I realise that props are immutable and state is mutable. I am getting clearer about their uses but there's one point I would like to clarify that I have not been able to find online.
So props can be passed from Parent to Child and when they are passed the name of the variable holding the props can be changed. When state or its values are passed to child components, a mechanism is also passed along to the child to be able to modify the state at the parent level by the child.
I hope this is correct as so far this is what I have understood about props and state.
Now I have a project where I have "App.js" which is the topmost parent with "Home.js" as its child. I plan to use state in both of them. But the state in App.js is something of a global state (and I don't want to use redux) and the state in Home.js would be the state that the application would use for its regular use.
Now both of them are being named using the state={} format (I am using class based components) and referred to as this.state but within their own components, which is working fine.
My question is about the format of naming state as state, is this mandatory or just a standard that developers are expected to follow?
I've tried to change the name of the state and it seemed to work but maybe I did something wrong as I didn't really expect it to work... so that's why I wanted to confirm if it's ok to change the name of state in a component to something like globalState or store
Any advise would be appreciated.
When writing Class Components, you can only update the state using the setState method and it will write the state to the state property.
While I suppose you could add a globalState getter property to the class which returns the value of state, this seems pointless.
Note that storing data in other properties instead of using setState to write to it will not trigger a re-render. You're just writing to a property on the object and not dealing in anything that React considers state.
When writing Function Components, state is handled with the useState hook and the processing of storing the state data is handled by React internals. The state value is then assigned to whatever variable you want to assign it to after reading the returned state array.
const [anyName, setAnyName] = useState(defaultValue);
const [anyOtherName, setAnyOtherName] = useState(otherDefaultValue);

Understanding the props concept in React

While I'm learning React, it's always said that you should keep your state in the parent component and pass them as props to its children.
But in the real world, whenever I start building a react app, I end up passing data from a child component to its parent.
For example, if I have to implement a form somewhere in my react app, I create an extra component for it (for example, FormComponent) and import it in my App component.
But now, I have to pass the form data form the FormComponent to the App component. In other words, from the child component (FormComponent) to the parent (App component).
And this was just an example. Usually, I always end up in situations where I have to pass data from child to parent rather than the other way around.
Do I misunderstand the concept of passing props completely?
If we stay with the above example, is my approach, right?
You can't pass data from child to parent as in React, The Data Flows Down.
So usually you pass callbacks from parent to its children, but the state remains in the parent.
This is commonly called a “top-down” or “unidirectional” data flow. Any state is always owned by some specific component, and any data or UI derived from that state can only affect components “below” them in the tree.
If you in a situation where the code becomes unreadable, unmaintainable, you should consider Lifting the State Up.
Often, several components need to reflect the same changing data. We recommend lifting the shared state up to their closest common ancestor.
Aside from it, a common anti-pattern is Prop Drilling, Context API, and many state libraries like Redux, MobX, and Recoil trying to solve such problems with some extra features.
1- In your case you can pass a function ( Example: handleSubmit() ) through props from parent to child.
And so when this function is called the child's data would be hundled from the parent.
you can use this doc page to inspire your code.
2- Otherwise you can use react redux, and then you can hundle all data at any component in your project using one global state called redux store.
want to learn more about react redux click here.
3- Have a nice day ^_^ .

Connecting components to redux global state v.s passing props down to children components in React-Native

I'm having trouble understanding when I should be passing props down to child components rather connecting those components directly to the state.
There are two ways that components in my project receive props:
1) Connecting a component (child or parent) to the global state of redux. This allows me to specify which parts of the global state my component will receive, but will re-render when a prop changes.
const mapStateToProps = state => ({
error: state.error,
progress: state.scan.progress,
latitude: parseFloat(state.enrollment.latitude),
longitude: parseFloat(state.enrollment.longitude),
});
export default connect(mapStateToProps)(MapViewHome);
2) Passing props down to child components in typical React-Native fashion.
<MapViewHome error={this.props.error} progress={this.props.progress} latitude={this.props.latitude} longitude={this.props.longitude} />
The reason I'm asking this is because I have parent component A which is connected to the state.
Parent component A passes prop x directly to child B which passes the same prop x to child C.
If prop x changes, this will cause components A, B, and C to re render. I could avoid the re rendering of components A and B by connecting child C directly to the state, so that it is the only component effected by the prop change.
But is it good practice to connect small child components to the global state like that? Why not just connect every component to the state?
This is a complex issue that the redux / react community discuss a ton.
You can probably find tons of Medium articles and React / Redux authors that discuss this.
But to give you a high level overview; If it seems like it's a pain to 'drill' props down from CompA -> CompB -> CompC -> CompD, store the data in redux.
If the data only needs to go from CompD -> CompE, you can pass it down as props.
You could store every prop that needs to be shared into redux as well. Just be aware of managing your redux object and try to normalize and manage the giant object.
Ultimately, it is up to you as the developer to make these decisions, as there is no silver bullet for this answer.
The main advantage of using stateless components is reusability. Using your example, a stateless version of MapViewHome could be used to display multiple maps of different lat/lngs on the screen at the same time since you can pass different props to each one. A stateful MapViewHome could only display one since it relies on global state.
So it's really up to you how to want to architect your components. I think it's a good idea to always start with stateless components but if your use case doesn't require reusability then using a stateful component is fine.
Typically, you don't need to use your store when you're capturing form data or if you're simply trying to hide and unhide elements. For example, you might want to have the local state change when you're typing data in a field and then you might use that local state to pass down to a thunk creator that you're dispatching. If you can provide a bit more context about what specifically you are trying to achieve here, I'd be happy to provide more guidance!

Re-rendering of connect()ed component based on mapStateToProps output

In a React + Redux based project, I have a connect()ed component which checks user permissions via an API fetch. Fetched permissions are stored in the Redux store.
The component basically looks like <Can check="...">...</Can>, which talks to our API (via Redux actions) to resolve the check. If the permission is granted, this.props.children is rendered, null otherwise.
For that, mapStateToProps() computes a passes prop from authorization data in the store, which is checked in <Can />s render() method. I utilize the ownProps parameter to mapStateToProps() to get the "stuff to check" and compute the passes flag.
There's a bit of caching going on so I don't re-fetch on every component mount, and it basically works. Sometimes, though, the component will not re-render when the passes prop updates to true (it will however render after navigating away - using react router - and back again, so basically if the component is re-mounted).
Do connect()ed components re-render if the output from mapStateToProps() changes? The docs for react-redux's connect() say this:
If ownProps is specified as a second argument, its value will be the props passed to your component, and mapStateToProps will be re-invoked whenever the component receives new props.
Does that mean that passing in ownProps changes the rendering to only re-render if props change, or in any other way? How can I understand the note regarding memoization/returning a function from mapStateToProps(), or is that not even related?
Thank you
Do connect()ed components re-render if the output from mapStateToProps() changes? The docs for react-redux's connect() say this:
Output from a function can’t change by itself. Something must trigger this function to be re-evaluated in the first place.
If Redux state changes, mapStateToProps is re-evaluated.
If props received from parent component are shallowly unequal (have changed) and you use ownProps argument, mapStateToProps is also re-evaluated.
If mapStateToProps returned shallowly equal values to its last call, then React Redux will skip rendering. If it returned shallowly unequal values, the wrapped component will be re-rendered. It is assumed that mapStateToProps itself is a pure function.
Sometimes, though, the component will not re-render when the passes prop updates to true
Please create a minimal project reproducing this and file an issue with the relevant code example.
How can I understand the note regarding memoization/returning a function from mapStateToProps(), or is that not even related?
Not related.
Several things to know here:
connect will shallow-compare the output of the last mapState call to the current mapState call. If nothing changed, it will not re-render the wrapped component.
By default, connect will only run mapState when the store notifies subscribers. However, if your mapState function is declared as taking two parameters, connect will pass in the wrapped component's props as the second arg, allowing you to do things like state.somePerItemData[ownProps.itemId]. It also then calls mapState any time the incoming props differ, as that may affect the output of mapState.
Reselect's default memoization only keeps a single cached value per selector function. If you have a component that is instantiated multiple times, and all instances are sharing the same selector function instance, then the selector's memoization probably won't work the way you want, because each component instance is probably calling it with different inputs (such as their own props). So, as a heavily advanced optimization, you can actually pass a factory function as the mapState argument, which could create a unique selector function instance for each component instance.
All that said, I'm afraid I don't have a specific answer for your actual question about the component not updated. I'd probably need to see the code in more detail.

ReactJS - Can I call setState immediately after renderComponent?

I'm using React for some client-side components. In some cases the components do not know their initial state, so I need a way to render the component and provide it with an initial state, externally.
renderComponent has a callback method:
ReactComponent renderComponent(
ReactComponent component,
DOMElement container,
[function callback]
)
Apparently:
If the optional callback is provided, it will be executed after the
component is rendered or updated.
Which is fine, but if I call setState in that callback, you will still see the initial (wrong) rendering for a split second before the component re-renders itself.
So my question is, can I do this:
var myComponent = React.renderComponent(...);
myComponent.setState({...});
...Safely? Can I presume that renderComponent has at least created the backing instance of the component in a synchronous fashion, so that there's something there to call setState on?
This pattern seems to work in my casual tests, using a localhost server, but I'm wondering if this is somewhat akin to using JS to modify the DOM without waiting for the ol' document-ready signal (i.e., prone to inconsistent behavior).
On the other hand, I could pass in a default state object as a prop, and then build the component so that it checks for that prop and populates its own state within componentWillMount or somesuch. Would this be more within the bounds of The React Way?
On the other hand, I could pass in a default state object as a prop, and then build the component so that it checks for that prop and populates its own state within componentWillMount or somesuch. Would this be more within the bounds of The React Way?
Yes. State should be treated like private instance variables for a component; you should never ever access them from outside the component, but just as it makes sense to pass options to an object constructor, it can make sense to specify some parts of the initial state of a component in props. React's uncontrolled components (with defaultValue) are an example of this sort of pattern.
When possible, it's usually nicer to keep the state stored higher up and prevent it from getting out of sync, much like React's controlled components. You can do this by making your component take an onChange callback that you then use to update your app's source of truth.
One other note: You should generally use getInitialState instead of componentWillMount; using the latter is currently allowed but may be deprecated in the future.

Categories

Resources