I'm new to react. In the examples I've found so far, using the react context api, you create your own context object and hand it as value to providers, etc. All of the examples seem to store the entire context (either as one object or the sum of its parts) in state and utilize that for requests. But what is the lifetime of the object? Is it only created once during page refresh, or is it recreated during each render cycle such that it can only survive via useState or useRef? In other words, is useState actually needed to store the context data between render cycles, or is useState only being used as a way to subscribe to changes?
This is the way I've been creating context based on the tutorials I've seen...
(To be clear, my question is about the lifetime of the "contextData" object in context.js, and whether rerenders or state changes or some other lifecycle event causes the script including the contextData instantiation and createContext to be rerun, or if some other weird interaction between createContext, contextData, and useContext is going on.)
context.js
import React from "react";
const contextData = {
prop1: "something",
prop2: "somethingElse",
func1: someFunction(stuff) {
prop1 += ".";
},
};
const myContext = React.createContext(contextData);
export {myContext};
function MyContextProvider({children}) {
render <myContext.Provider value={myContext}>{children}</myContext.Provider>;
}
export default MyContextProvider;
index.js
...
root.render(
<MyContextProvider><App /></ContextProvider>
);
SomeComponent.js
import {useContext} from "react";
import {myContext} from "./context"
function SomeComponent() {
const ctx = useContext(myContext);
return <span>{ctx.prop1}</span>
}
This is a bit tautological, but the context object is re-created whenever the function that creates it runs again. The function will (almost always) be either a component or custom hook.
The React philosophy is that the view should flow from the state, so state changes result in (functional) components running again to determine how to change the view (if at all). The same is true if state is included in a context object - if a state setter inside the context object is called, the functional component enclosing the initializer of that state runs again, resulting in a new object being passed down to consumers.
In other words, is useState actually needed to store the context data between render cycles
Yes.
But what is the lifetime of the object?
One particular context object will generally be garbage collected once there's been a re-render and no children reference the old object in a stale closure.
All context objects will get garbage collected only after the component surrounding the context provider unmounts.
With regards to the code in the question - by mutating an object that gets passed down, you're breaking the standard React workflow, which should avoid mutation for those sorts of values. It would be far better to call .createContext in a parent component and to pass down a state setter that changes the prop1 property.
That said, if you don't do that - the top level of context.js only runs once, so you only create the object once, so that one object will exist forever. (which could be a mistake in case you ever change the code so that you want to use the context somewhere else too - the context value will include the old mutations)
Related
I just start learning how redux work :
What redux mean by there is one particular function take the whole state of the application and action is been dispatched and return the whole new state of the application?
is this is visible on Enterprise level application Single Page Application ERP system or I have misunderstood something?
also how the function be pure and change in the previous state " use ref of the previous state and add to it new data then return this as an object?
if it is not is that will not affect the speed of the system to copy each time the whole system state for ERP system?
the course in which I get this info 1
the course in which I get this info 2
Redux in general
In redux, your app's state is represented as a single object which is passed to your app.
This single object is constructed by a single function which is called reducer. This function takes the current state and current action as arguments and returns a new object which represents the new state changed after the action is reduced.
It's kinda similar to Array.prototype.reduce where you get current accumulator (current state) current array entry (current action) and return new accumulator (new state) depending on what is the current accumulator and entry (state and action).
Combining reducers
Nevertheless, this doesn't mean you need to put all of your app's logic in one function. Redux has a helper combineReducers to let you write reducers for different parts of your state and then combine them into single reducer that constructs a single state object.
For example for a blog app with posts and comments, you can use it like that:
import { combineReducers, createStore } from 'redux';
import postsReducer from './postReducer';
import commentsReducer from './commentsReducer';
const rootReducer = combineReducers({
posts: postsReducer,
comments: commentsReducer,
});
const store = createStore(rootReducer);
Where postReducer and commentsReducer are two separate reducers each handling their part of the state.
I am working on a React/Redux application. Let's say I have a component that receives two props from it's container:
// NewsFeedItem.js
class NewsFeedItem extends React.Component {
render() {
const user = this.props.user;
const item = this.props.item;
return <p>{user.name} - {item.date}</p>;
}
}
const mapStateToProps = (state) => ({
user: state.user,
item: state.user.items[0],
});
export default connect(mapStateToProps)(NewsFeedItem);
How can I make sure this Component doesn't update if I make a change to the 2nd or 3rd item in the items list?
Pasting in my answer from the associated Redux issue for completeness:
First, there's nothing wrong with a React component re-rendering - that's how React works in the first place. If a component re-renders with the same data and produces the same render output, then React just doesn't update the DOM. Now, sure, that's considered a "wasted" re-render that probably could have been avoided, but it's only a problem if you're trying to seriously optimize performance.
Second: yes, if you're correctly immutably updating your state, then an update to state.user.items[3] should result in new references for items, user, and state, and passing user as a prop to the component like that would cause it to re-render. However, in your specific example, that component is only using user.name. so there's no reason to pass the entire user object as a prop - try passing just name : state.user.name instead.
Third, you could split up the storage of the user attributes and their associated items into separate parts of the state, so that an update to an item doesn't cause an update to the associated user object. See the Normalizing State Shape page in the Redux docs as an example.
Finally, you could always implement a custom shouldComponentUpdate in your component, and do an additional check there to see if the component should skip re-rendering.
From what I've understood from Dan Abramov's egghead video 'javascript-redux-colocating-selectors-with-reducers' and some of his tweets, it is a good practice to use a selector to map the state to a prop and remove this logic from the Component and placing it in the Reducer (where the state is managed).
Although this makes all the sense, it also causes my component to render everytime a new state is added to the store, even when only a non related property of the state object was changed. Is there a way to overcome this without using reselectors, which might be a bit overkill for the simpler cases?
As you may know, mapStateToProps is called every time your store is updated.
Whether the component will re-render depends on what mapStateToProps returns. (Actually, it depends on the combined props object returned by mapStateToProps and mapDispatchToProps.)
React Redux (the library that provides the connect function) makes a shallow equality check on the returned object and the last returned object. If the equality check succeeds (i.e. the previously returned object is determined to be equal to the next returned object), the component will not re-render. If the check fails, the component will re-render.
For example, let's say you always return the following object from mapStateToProps:
{
items: [],
}
This object will never be equal to itself ([] === [] returns false because they're different arrays). The equality check will thus fail and the component will re-render.
However, React Redux performs a more complex equality check that that (the implementation of its shallowEqual function can be found here).
For example, even though { a: 'b' } === { a: 'b'} returns false (they're different objects), shallowEqual will pass them off as equal. This is because shallowEqual will compare each key of the returned object with each key of the previously returned object, but only one level deep. More details can be found in the implementation I linked to above.
In summary, if you don't want your component to re-render, you'll need to make sure that the equality check succeeds.
You can:
Save the returned object into the state using a reducer
Cache the result using Reselect
Implement shouldComponentUpdate in the component by hand
These suggestions come straight from Redux's FAQ page: https://redux.js.org/docs/faq/ReactRedux.html#react-rendering-too-often
You can also make sure your mapStateToProps function returns objects that are considered equal by shallowEqual (e.g. objects without arrays, and only one level deep).
For simplicity, I would opt for Reselect.
The short answer is: No it is not.
But there is common mistake that causes unnecessary renders of components when you're using selectors. You should always make sure to define your selector once. So what does it mean?
When you are using connect method, you can pass mapStateToProps method as an argument. Object returned by this method will be passed as props to your component and if you define your selector inside this object it will be redefined each time your component receives a prop. Here is an example for that:
Defining your selector like this could cause your component to render unnecessarily. This is because each time you pass a prop to your component you're basically redefining getSettings method.
#connect(state => ({
getSettings: ()=>'sample output',
}))
class Sample extends React.Component {}
Correct way is the define your selector like this, so that it'll be only created once and the reference passes through your mapStateToProps argument.
const getSettings = () =>'sample output';
#connect(state => ({
getSettings,
}))
class Sample extends React.Component {}
I would like many different redux actions in my app to all trigger common functionality in a specific reducer. I would like to avoid having to either repeat some flag in every action creator (like doThing: true) that the reducer looks for. I also don't want to have to have the reducer just look for every individual action that falls into this category, since that also requires someone to remember to do this every time they add a new action, like adding the flag.
I was thinking of dispatching a second action every time one of these actions is going to be dispatched. This would not be hard to do, but I'd rather not have 2 actions dispatched every time one thing happens. It seems like it would pollute the state history.
Is there a common way of solving this problem?
For more context to my specific problem, the specific feature is related to the API client my app uses to talk to our API. On every successful response, we'd like to do something in a reducer to update the state, and on every failed response, we'd like to do something else.
There are many different success and failure actions (such as ITEM_FETCH_SUCCESS or WIDGET_UPDATE_FAILURE), and adding a flag to all of them would be hard to remember to do when new ones are added.
Since all api requests go through a single function, that function COULD dispatch generic REQUEST_SUCCESS and REQUEST_FAILURE actions. But this would mean every response from the server would dispatch 2 actions (REQUEST_SUCCESS and ITEM_FETCH_SUCCESS). This is obviously not ideal since it would mean many more actions in my state history.
Assuming the generic REQUEST_SUCCESS and REQUEST_FAILURE actions are updating their own specific portions of the state-tree then it is fine to dispatch them as distinct actions. Doing this does not necessarily imply the pollution of your state history but can simply be a better description of the app's intentions.
ITEM_FETCH_SUCCESS: Change state for item
REQUEST_SUCCESS: Change state for request
WIDGET_UPDATE_FAILURE: Change state for widget
REQUEST_FAILURE: Change state for request
You can see that whilst the actions are intimately related, they are not necessarily the same thing as they change different parts of the state tree.
Accepting this, the question is: How best to implement the action-pairs so that adding new actions does not mean remembering to add its corresponding REQUEST_* partner?
I would consider applying a simple redux middleware component. This could intercept the return from your api and dispatch the appropriate REQUEST_* action automatically.
Here is an example from some live code. This middleware intercepts a disconnect event raised by a websocket and automatically dispatches a custom action as a result. It at least shows the principle:
//Dispatch a disconnect action when the websocket disconnects
//This is the custom action provided by the middleware
import io from 'socket.io-client'
import { actions } from './action'
const websocket = ({ websocketUrl }) => store => {
const socket = io(websocketUrl)
socket.on('disconnect', () => store.dispatch(actions.disconnect()))
}
export default websocket
//Apply the custom middleware via the redux createStore function
//Also include the thunk middleware because it is useful
import { applyMiddleware } from 'redux'
import thunk from 'redux-thunk'
import websocket from './middleware'
function websocketize (opts) {
return createStore => (reducers, initial, enhancer) => {
const middleware = applyMiddleware(thunk, websocket(opts))
return createStore(reducers, initial, middleware)
}
}
export default websocketize
// Create the top-level redux store passing in the custom middleware enhancer
const opts = {websocketUrl: env.WEBSOCKET_URL}
const store = createStore(reducers, websocketize(opts))
This implementation keeps everything inside your reducers as opposed to having logic outside in an interception(middleware). Both ways are valid.
Try a sub-reducer pattern. I usually feel gross when I see it used(because it is usually used wrong), but your situation sounds perfect.
Extract duplicate functionality out of your reducers to one single
sub-reducer.
Then pass that reducer as a function to all others that need it.
Then pass the action and state onto the sub-reducer.
The sub-reducer does it's thing and returns that slice of state to
your parent reducer to allow you to do whatever you want with it
there (ie return it, mutate some more, some logic).
Also if you are tired of worrying about typing out "all the stuff" for async then I highly recommend you try out redux-crud.js
It also is possible and a simple way to do that would be to give every action to one reducer and let it do that common mutation, in a single case:
case actionOne
actionTwo
actionThree
actionFour: {
//do common stuff here
}
. But you said it is not duplicated, it is similar, which means your case becomes complicated by branching logic. I also don't recommend this. Keep cases simple so you can easily catch invalid mutations. This should be a super power of redux that it is easy to catch mutation errors. And for this reason and many others I don't recommend normalizing data in the front end.
I am trying to update the reducer state using :
store.dispatch(NameOftheReducer(data)).
It calls the Action creator but it does not update the reducer state. I dont want to create any React component from where i want to dispatch the state change. Is there any way to do so..Thanks in advance
Lets assume the store is stored stored somewhere global
then you can just
store.dispatch({
type: "SOME_ACTION",
value: "value"
})
if you have some actionCreator like:
function someAction(value) {
return {
type: "SOME_ACTION",
value: value
}
}
And and now you can use it with dispatch:
store.dispatch(someAction("some value"))
Did I missed something?
I think you misunderstood how component and redux state relates.
redux state change are done through actions regardless of which component is actually 'using' this state.
As long as you have an action creator defined somewhere, and you reducer handle the corresponding action.type, then you can use this action creator in whichever component.
There is no benefit of using store directly. store is stored in context, and it is generally considered bad practice using context. The nice thing about redux is that it takes care of this by giving you Provider and connect.
Except when initializing the app, you should always use mapDispatchToProps when you want to use action creators in your component.