How to get updated React Context with static variable? - javascript

So, I have ClientContext with default value:
export const defaultContext = {
test: "Hello"
};
export const UserApplicationContext = React.createContext(defaultContext);
And then in my child component I am updating this value:
contextDefaultData = this.context,
contextData = {
...contextDefaultData,
test: "New"
}
};
<UserApplicationContext.Provider value={contextData}>
<App/>
</UserApplicationContext.Provider>
Now, question: In App I can access updated value via UserApplicationContext.Consumer component but I can't access updated value via static like this:
import UserApplicationContext from './UserApplicationContext'
static contextType = UserApplicationContext
So, this.context will point to default value, but not to updated one.
How may I access updated value without exporting new Context?
Thanks!

I guess that's a limitation of the context api?
In order to access the most recent version of the context, you have to be inside of a component that is rendered inside of the provider's component tree and you either have to consume the context like <UserApplicationContext.Consumer> or if you wanna use hooks/functional components, you can do it in a nicer way like const mostRecentContext = useContext(UserApplicationContext).
If you try to import the context like that and access it without a consuming it via a .Consumer or the useContext hook, it will always be whatever the value use passed to React.createContext().

Related

React Context Consumer does not update with Hooks state

I am using React Context Provider and Consumer in order to pass data from my App component to its children.
My Context is called CurrentUserContext. I declare it like this:
const CurrentUserContext = React.createContext();
and export it like this:
export default CurrentUserContext;
I am wrapping my App component with CurrentUserContext.Provider and passing as the value a stateful data I declare like this:
const [loggedUser, setLoggedUser] = useState(null);
When I want a child component to use the data, I wrap it with CurrentUserContext.Consumer.
It all works perfectly fine, but for some reason, when I update loggedUser using setLoggedUser (in useEffect for example), the consumers does not get re-rendered.
Does anyone know why?
I want every time that I update loggedUser using setLoggedUser, the relevant components (the consumers) will re-render.
Does anyone know what doesn't it work?
Thank you!

Updating my imported variable's value in React.js

I am refractoring an app I've build using React.js. I am exporting a variable from the global scope of Spotify.js and importing it in two other files App.js and Button.js.
After calling a function from Spotify.js that sotres a new value to the variable, It's new value is exported to 'Button.js' but stays an empty string in 'App.js'.
Your help would be appriciated :)
export let userAccessToken = '';
export const Spotify = {
...
getUserAccessToken (){
//stores a new string to userAccessToken.
}
}
import {userAccessToken, Spotify} from '../../util/Spotify';
export class App extends React.Component {
//a conditional rendering happens depending on if(!userAccessToken)
}
import {userAccessToken, Spotify} from '../../util/Spotify'
export class Button extends React.Component {
componentDidMount() {
if (!userAccessToken) {
console.log(`Button's UAT before calling the fn: ${userAccessToken}`)
Spotify.getUserAccessToken();
console.log(`Button's UAT after calling the fn: ${userAccessToken}`);
}
}
...
}
This is not how you share data between react components.
Use react context or pass props between components
You could use react context to share data or simply pass it as props (if the components are closely related in the component tree)
The only thing I can recommend is to export the userAccessToken, something like this, but you can't change its value outside the module
export const Spotify = {
...
getUserAccessToken (){
//stores a new string to userAccessToken.
}
}
...
}
const accessToken = Spotify.getUserAccessToken();
export const userAccessToken = accessToken;
If you got to read this question I solved it.
Turns out I should have called my method Spotify.getUserAccessToken() from App.js using the react lifecycle method componentDidMount().
The export and import methods are like snapshots of the module and therefore when I exported the variable userAccessToke from Spotify.js before calling the my method I imported an empty string and it did not update in all files.
Thanks Jørgen and joseluismurillorios for your support and time spent answering :)

Alternative to getState for recurring variables in Redux

I use getState to get a clientId that I need to include in every api call right now. Problem is that this interrupts data flow as the app doesn't rerender when clientId changes. Do I have to manually get the clientId in every component that I need to include it in or is there a better alternative? (clientId is also in store and is fetched first when the user logs in)
Sounds like a good candidate for the use of Context.
Here's a fictitious example of how you can set the client ID at a high level but reference it in nested components without having to query the Redux store each time using Hooks:
App
const ClientContext = React.createContext(null);
function App(props) {
return (
<ClientContext.Provider value={props.clientId}>
<MyApiComponent />
</ClientContext>
)
}
const mapStateToProps = getState => ({
clientId: getState().clientId
})
export default connect(mapStateToProps, {})(App);
So we only need to connect the App to the store to retrieve the client ID, then using Context we can make this value accessible to nested components. We can make use of useContext to pull that value in to each component
function MyApiComponent() {
const clientId = useContext(ClientContext);
...
return <MyNestedApiComponent />;
}
function MyNestedApiComponent() {
const clientId = useContext(ClientContext);
...
}
Whether it's function or class components you are using, the principle is the same - Context is used to share global state down to nested components.

Access React context from regular function

Is it possible to access React context from a non-React helper function?
For example, I have a context provider that saves the root slug for a case study page when the app loads. I then need to build URLs, consuming the context provider at various places around my app. So I've created a helper function to do so:
components/caseStudies/index.jsx
import getCaseStudyPath from '../../lib/helpers/getCaseStudyPath';
const CaseStudies = ({ caseStudy }) => {
// caseStudy.slug === 'test-case-study'
const caseStudyPath = getCaseStudyPath(caseStudy.slug);
return (
<a href={caseStudyPath}>Test</a>
);
};
lib/helpers/getCaseStudySlug
export default (slug) => {
// Get caseStudyRootSlug from React context
return `/${caseStudyRootSlug}/${slug}`;
// For example, this returns '/case-studies/test-case-study'
};
I know I can just consumer the context provider in my React component, or even as a HOC, and just pass the value to the helper function, but I need to use this helper function throughout my app and don't want the overhead of setting up a consumer every time I want to use it.

React Context Folder hiarchy / architecture?

I'm currently looking at implementing Context into one of our apps over Redux, but, I can't seem to find any information on what would be the best structure for large scale apps?
Redux has a defined way to create reducers, actions, etc. With Context, all I've found are the generic "create a provider, put state and methods all on the same file, and then use a consumer".
TL;DR Is there a way to build a hiarchy that is beneficial for long term, and large scale applications with React Context?
Edit: I guess this is incorrect to think of them having a similar structured relationship. Unfortunately, I'm not able to use Redux because of AEM's limitations. Context does work however, so I wanted to hopefully be able to build some structure with that.
First of all, I don't think there is necessarily a right or wrong answer to this question, but I will just give you my two cents.
I am currently refactoring a web application which serves several millions of sessions per month and am testing a redux and context version on internal stage servers.
Important notices:
I am using a mono-store approach
It's not an app which constantly has global store updates
To the folder structure. I like to keep my store in the root of the project. For a react app based on react-create-react-app that would be the /src and it basically consists of the following files:
index.js // everything gets "bundled" here
initialState.js // provides the store with intial state e.g. from server, cache etc.
methods/*.js // contains split methods based on the part of the app that they are used in (if it can be split into separate parts)
Ergo my index.js is as simple as:
import React from 'react';
import storeMethods from './methods';
import initialState from './initialState';
// to start of experimenting with context
// i would keep all read and write key value
// pairs right here and split as the codebase
// grows and you realize you need more space
export const store = {
...initialState,
...storeMethods
}
export const StoreContext = React.createContext(store)
storeMethods is a bundled export from all methods in the methods/ folder. Basically it's just another object of containing keys which values are functions like so:
export const methods = {
showNavBar: function() {
this.setState({ navBarOpen: true })
}
}
initialState is as much as the representation of key value pairs that are required to render the base content of the app and or never change. Basically some global settings. Initialstate coming from the server, is being added to the store in the constructor of my App, right before I bind the lexical scope.
The store get's thrown into the state of the relevant outermost React Component and is used as the app state, where I bind the store's scope to the React Components lexical scope.
Then I have a higher order component withContextConsumer which is used to wrap any React component which needs access to the state. The HOC distributes the subscribed keys down as props to the wrapped component and can be consumed as read only or write.
No matter how you end up using Context, don't forget, that any Consumer will have it's render method automatically called if the Context Store is being updated. To avoid that on a simple oldProps !== newProps level, you can use PureComponents. For more complex diffs you can use the lifecyclemethod shouldComponentUpdate
edit
Basic App Structure
App.js:
import React, { PureComponent } from 'react'
import { StoreContext, store } from './store'
import { bindScopeToFunction } from './helpers'
class App extends PureComponent {
constructor(props) {
super(props)
const { initialState = {} } = props
const boundStore = bindScopeToFunction(store, this)
this.state = {...boundStore, ...initialState}
}
render () {
return(
<StoreContext.Provider value={this.state}>
// in here you render all your app
// routing, childcomponents etc
// in any component where you need access
// to the global store
// wrap it in <StoreContext.Consumer> it has
// the whole store as render prop
</StoreContext.Provider>
)
}
}
Working basic example can be found here https://codesandbox.io/s/pm85w4y6xm

Categories

Resources