Can I use HOC to wrap component with graphql and redux - javascript

I have plenty of template components, they resemble each other in way they are used.
Before being rendered to page, template components get wrapped in graphql and connected to redux.
I want to create a HOC to wrap my templates, so that I do not create a new container each time to connect template to data.
Like so:
Here is my page component, where I try to wrap the AppointmentsListTemplate template with gqlList HOC:
import React from 'react'
import { AdminTemplate, AppointmentsListTemplate } from 'components'
import { gqlList } from 'containers'
import {qyListAppointments} from 'services/gqlQueries/Appointments'
const AppointmentsListTemplateWrapped = gqlList(AppointmentsListTemplate, qyListAppointments)
const AdminAppointmentsPage = (props) => {
return (
<AdminTemplate>
<AppointmentsListTemplateWrapped />
</AdminTemplate>
)
}
export default AdminAppointmentsPage
And here is my gqlList HOC:
import React, {Component} from 'react'
import { graphql } from 'react-apollo'
import { connect } from 'react-redux'
import { saveQueryVars } from 'store/helper/actions'
const gqlList = (WrappedComponent, gqlQuery) => {
const GQL = graphql(gqlQuery)(WrappedComponent)
return connect(null, {
saveQueryVars,
})(GQL)
}
export default gqlList
But graphql connector part throws me this error:
Uncaught TypeError: Cannot read property 'displayName' of undefined
at getDisplayName (react-apollo.browser.umd.js:250)
at wrapWithApolloComponent (react-apollo.browser.umd.js:266)
at new eval (gqlList.js:22)
at eval (createClassProxy.js:95)
at instantiate (createClassProxy.js:103)
at Unknown (eval at proxyClass (createClassProxy.js:NaN), :4:17)
at eval (ReactCompositeComponent.js:303)
at measureLifeCyclePerf (ReactCompositeComponent.js:73)
at ReactCompositeComponentWrapper._constructComponentWithoutOwner
What am I doing wrong?

There doesn't seem to be anything wrong with your code, as far as I can tell. I would also rule out redux as the source of the error. But here's what I suggest:
Check your GraphQL query (gqlQuery) so that it gets what it needs and it can return what you need. I suspect it requires some parameters but doesn't get the right type of a parameter - resulting in a Type error.
Here's an example (without redux) of how to pass parameters:
const Data = graphql(fetchData, { options: {variables: {id: this.getId()} }})(Thing);
Here, fetchData requires the id of a Thing and returns data about that thing. Then you can render <Data/>.
You might want to improve your question by adding the query and variables (saveQueryVars) to it. Also, mention the version of react-apollo because that's the module throwing the error. As a side note, the error message coming from the Apollo client is not very helpful.

You can chain them together:
import { changeFoo } from './myReduxActions';
const QUERY = gql`{ FindSomething { Id, Value }}`;
const myComponent = connect(
store => ({
foo: store.fooReducer.foo
}),
dispatch => ({
changeFoo: (val) => dispatch(changeFoo(val))
})
)(graphql(QUERY)(
{props.data.loading && return <div>Loading...</div>}
let myNewFoo = 'abc';
return props.data.FindSomething ?
<div>{`Id is ${props.data.FindSomething.Id}, redux store value foo is ${props.foo}`}
<div onClick={() => props.ChangeFoo(myNewFoo)}></div></div> :
props.data.error ? <div>Error {props.data.error}</div> : null;
));
So you could do connect(graphql(pureComponent))) or written as connect => graphql => component. You can change the order of graphql and connect.

Related

Reading JSON file into React Context Provider with Typescript

My React Typescript app has a Context Provider DataProvider that is to read a value from a JSON file and provide it in the Context useData(). I am trying to do the read synchronously to avoid having to deal with a isLoading since this is a tiny local JSON file.
Is there a recommended way to read the JSON file synchronously inside a Context Provider?
I tried the following using node-sync, but its giving a Typescript error
Object is possibly 'undefined'.ts(2532)
on data at line
return data.find(...
Tried changing it to
return data?.find(...`
but now the error is
Property 'find' does not exist on type 'never'.ts(2339)
import React, {
createContext,
useContext,
Consumer,
Context,
ReactNode,
useMemo,
} from 'react';
import Sync from 'sync';
export interface DataProviderProps {
children: ReactNode;
}
export interface Data {
secretNumber?: string;
}
// #ts-ignore
const DataContext: Context<Data> = createContext<Data>();
export function DataProvider({ children }: DataProviderProps) {
const secretNumber = useMemo(() => {
// Read from JSON file
const contractFile =
process.env.REACT_APP_WORLD === 'main'
? '../main.json'
: '../test.json';
let data;
Sync(function () {
data = import(contractFile);
});
return data.find( // <=== TS error: Object is possibly 'undefined'. ts(2532)
({ name }: { name: string }) => name === 'elonmusk',
)?.secretNumber;
}, []);
const states = useMemo<Data>(() => {
return {
secretNumber,
};
}, [secretNumber]);
return (
<DataContext.Provider value={states}>
{children}
</DataContext.Provider>
);
}
export function useData(): Data {
return useContext(DataContext);
}
export const DataConsumer: Consumer<Data> = DataContext.Consumer;
array.find() returns undefined If no values satisfy the testing function, from https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/find, so just add (!) after the array.find()! fxn to ascertain a value would be returned.
sample code stub
data.find(todoCodeStubhere)!

'MyVariable is declared but its value is never read ' When i using module type in javascript

MyVariable is connect()
import {connect} from './store.js'
//message Error returned when I hovered: connect is declared but its value is never read
And store.js gets {connect} from core.js
//store.js
const {attach, connect,dispatch}=createStore(withLogger(reducer))
//...
export { connect }
And function createStore I get it from core.js
//core.js
export function createStore(reducer) {
return {
connect(selector = state => state) {
return component => (props, ...args) =>
component(Object.assign({}, props, selector(state), ...args))
},
}
}
I write core.js like a library.
I open it on live server and it sends me a message:
Uncaught ReferenceError: Cannot access 'MyVariable' before initialization
Actually I have fixed this error by a different way but I still want to know Where is the problem.
So hope guys help me :((

React Redux Reselect - Undefined State

I have the following code in my react/redux app:
import { createSelector } from 'reselect';
const selectMembers = state => state.membersData;
const makeSelectLessonSets = createSelector(
selectMembers,
substate => substate.get('myData').toJS()
);
The problem is that sometimes myData is not yet defined, so substate.get('myData') will not get anything. And since I try to call toJS() on it, it shows the error: undefined is not an object.
But I don't know how to check if substate.get('myData') has returned a valid object inside createSelector before I call toJS() on it.
Can you please help with it.
I added ? before the dot this will not throw an error
import { createSelector } from 'reselect';
const selectMembers = state => state.membersData;
const makeSelectLessonSets = createSelector(
selectMembers,
substate => substate?.get('myData')?.toJS()
);

React & Redux: Uncaught TypeError: (0 , _reactRedux.connect) is not a function

I am new with redux, react and parceljs. I'm currently experimenting with this 3 stuff by doing the redux tutorial. And after parceljs did it job, when I went to the browser I got this error on the console:
Uncaught TypeError: (0 , _reactRedux.connect) is not a function
The code right now is
import { connect } from 'react-redux';
const AddTodo = ({dispatch}) => {
.
.
.
}
export default connect()(AddTodo)
I changed:
import { connect } from 'react-redux';
to:
import { connect } from 'redux';
and gave me basically same error.
What should I do in this case?
I checked the documentation and issues and the only issues I found about connect() is that you cannot use it as a decorator but that's it. Am I missing something I don't know yet?
(Sorry for my bad grammar in English, not my first language)
To use connect, you need to bind the component not an object. Thus, you may change your todo
const AddTodo = {
With:
const AddTodo = () => { // a stateless component
// now, you can return the object
return ( // just an example
<div>
<input type="text" />
</div>
)
}
And the connect is imported from react-redux:
import { connect } from 'react-redux'; // this is correct

this.props.relay.forceFetch() error: Cannot read property 'getHostNode' of null

I have a page component that I need to be fetched from server every time it is loaded, guaranteeing fresh data.
Here is my component code:
import React, { Component } from 'react';
import PropTypes from 'prop-types';
import { createFragmentContainer,
graphql } from 'react-relay';
import Listing from '../Listing/Listing';
class Page extends Component {
componentWillMount = () => {
this.props.relay.forceFetch();
}
render = () => {
return (
<Listing
data={this.props.viewer.users}
/>
);
}
};
export default createFragmentContainer(Users, graphql`
fragment Users_viewer on Viewer {
users {
name
username
email
}
}`
);
When the component is loaded, I´m getting the following error:
Uncaught (in promise) TypeError: Cannot read property 'getHostNode' of null
at Object.getHostNode (ReactReconciler.js:62)
at ReactCompositeComponentWrapper.getHostNode (ReactCompositeComponent.js:383)
at Object.getHostNode (ReactReconciler.js:62)
at ReactCompositeComponentWrapper.getHostNode (ReactCompositeComponent.js:383)
at Object.getHostNode (ReactReconciler.js:62)
at ReactCompositeComponentWrapper._updateRenderedComponent (ReactCompositeComponent.js:755)
at ReactCompositeComponentWrapper._performComponentUpdate (ReactCompositeComponent.js:723)
at ReactCompositeComponentWrapper.updateComponent (ReactCompositeComponent.js:644)
at ReactCompositeComponentWrapper.performUpdateIfNecessary (ReactCompositeComponent.js:560)
at Object.performUpdateIfNecessary (ReactReconciler.js:156)
at runBatchedUpdates (ReactUpdates.js:150)
at ReactReconcileTransaction.perform (Transaction.js:143)
at ReactUpdatesFlushTransaction.perform (Transaction.js:143)
at ReactUpdatesFlushTransaction.perform (ReactUpdates.js:89)
at Object.flushBatchedUpdates (ReactUpdates.js:172)
at ReactDefaultBatchingStrategyTransaction.closeAll (Transaction.js:209)
at ReactDefaultBatchingStrategyTransaction.perform (Transaction.js:156)
at Object.batchedUpdates (ReactDefaultBatchingStrategy.js:62)
at Object.enqueueUpdate (ReactUpdates.js:200)
at enqueueUpdate (ReactUpdateQueue.js:24)
at Object.enqueueSetState (ReactUpdateQueue.js:218)
at ReactRelayQueryRenderer../node_modules/react/lib/ReactBaseClasses.js.ReactComponent.setState (ReactBaseClasses.js:64)
at onError (ReactRelayQueryRenderer.js:196)
at onRequestError (RelayNetwork.js:86)
at <anonymous>
BTW: This page component is being called using React Router V4.
How to solve that issue and guarantee that the page will get fresh data from server on reload ?
Instead of using createFragmentContainer I recommend using createRefetchContainer. The name is a bit misleading. It is a component that handles refetching and the original fetch. It should be similar to
export default createRefetchContainer(Users, graphql`
fragment Users_viewer on Viewer {
users {
name
username
email
}
}`,graphql`
query PageQuery {
viewer {
...Page_viewer
}
}
`
);
Notice that createRefetchContainer takes three parameters instead of the two you are used to in createFragmentContainer. Your error is saying that that specific prop is not available because you did not set up a "refetch container." Therefore, it cannot find the relevant component (i.e. node) to update.
Then it will load the data on load. If you want to refetch the data, call this.props.relay.refetch({}, null, ()=> console.log('refetch done'), {force: true}) in componentDidMount(). This will be a cleaner, safer, and better performing

Categories

Resources