I am running a pattern like so, the assumption is that SearchResultsContainer
is mounted and somewhere a searchbar sets the input.
class SearchResults {
render() {
return(
<ResultsContext.Consumer value={input}>
{input => <SearchResultsContainer input=input}
</ResultsContext.Consumer>
)
}
class SearchResultsContainer
componentDidUpdate() {
//fetch data based on new input
if (check if data is the same) {
this.setState({
data: fetchedData
})
}
}
}
this will invoke a double fetch whenever a new context value has been called, because componentDidUpdate() will fire and set the data. On a new input from the results context, it will invoke componentDidUpdate()
, fetch, set data, then invoke componentDidUpdate()
, and fetch, then will check if data is the same and stop the loop.
Is this the right way to be using context?
The solution I used is to transfer the context to the props through a High Order Component.
I have used this very usefull github answer https://github.com/facebook/react/issues/12397#issuecomment-374004053
The result looks Like this :
my-context.js
:
import React from "react";
export const MyContext = React.createContext({ foo: 'bar' });
export const withMyContext = Element => {
return React.forwardRef((props, ref) => {
return (
<MyContext.Consumer>
{context => <Element myContext={context} {...props} ref={ref} />}
</MyContext.Consumer>
);
});
};
An other component that consumes the context :
import { withMyContext } from "./path/to/my-context";
class MyComponent extends Component {
componentDidUpdate(prevProps) {
const {myContext} = this.props
if(myContext.foo !== prevProps.myContext.foo){
this.doSomething()
}
}
}
export default withMyContext(MyComponent);
There must be a context producer somewhere :
<MyContext.Provider value={{ foo: this.state.foo }}>
<MyComponent />
</MyContext.Provider>
Here is a way to do it that doesn't require passing the context through props from a parent.
// Context.js
import { createContext } from 'react'
export const Context = createContext({ example: 'context data' })
// This helps keep track of the previous context state
export class OldContext {
constructor(context) {
this.currentContext = context
this.value = {...context}
}
update() {
this.value = {...this.currentContext}
}
isOutdated() {
return JSON.stringify(this.value) !== JSON.stringify(this.currentContext)
}
}
// ContextProvider.js
import React, { Component } from 'react'
import { Context } from './Context.js'
import { MyComponent } from './MyComponent.js'
export class ContextProvider extends Component {
render(){
return (
<MyContext.provider>
{/* No need to pass context into props */}
<MyComponent />
</MyContext.provider>
)
}
}
// MyComponent.js
import React, { Component } from 'react'
import { Context, OldContext } from './Context.js'
export class MyComponent extends Component {
static contextType = Context
componentDidMount() {
this.oldContext = new OldContext(this.context)
}
componentDidUpdate() {
// Do all checks before updating the oldContext value
if (this.context.example !== this.oldContext.value.example) {
console.log('"example" in context has changed!')
}
// Update the oldContext value if the context values have changed
if (this.oldContext.isOutdated()) {
this.oldContext.update()
}
}
render(){
return <p>{this.props.context.example}</p>
}
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With