Cannot read property 'name' of null (react-admin) - react-admin

I have the most simple code possible to check react-admin:
import React, { Component } from "react";
import buildGraphQLProvider from "ra-data-graphql-simple";
import { Admin, Resource } from "react-admin";
import posts from "./routes/posts";
class App extends Component {
constructor() {
super();
this.state = { dataProvider: null };
}
componentDidMount() {
buildGraphQLProvider({
clientOptions: { uri: "https://countries.trevorblades.com" }
})
.then(dataProvider => this.setState({ dataProvider }))
.catch(e => console.log(e.message));
}
render() {
const { dataProvider } = this.state;
if (!dataProvider) {
return <div>Loading</div>;
}
return (
<Admin dataProvider={dataProvider}>
<Resource name="posts" {...posts} />
</Admin>
);
//or, directly return <Admin dataProvider={dataProvider} />
}
}
export default App;
but I always get the same error in console: Cannot read property 'name' of null
My dependences are:
"graphql": "14.6.0",
"graphql-tag": "2.10.1",
"ra-core": "3.1.4",
"ra-data-graphql-simple": "3.1.4",
"react": "16.12.0",
"react-admin": "3.2.0",
"react-apollo": "3.1.3",
"react-dom": "16.12.0",
"react-scripts": "3.0.1"
What I'm doing wrong??

I had the same problem.
The issue was that I was missing Mutation type in my graphql schema, therefore the check
type.name !== schema.mutationType.name
threw an error, because schema.mutationType was undefined
Make sure you have a Mutation type in your schema even if it's an empty one

Maybe you wanted to write?
<Resource name="posts" />
Have you read the docs?
https://marmelab.com/react-admin/Resource.html

Related

Why is my navigation ref not ready in React Navigation 6 with Redux?

In React Navigation 6, my research shows that to navigate without a prop I should make a reference and use createNavigationContainerRef. I'm able to pass down the screen name to my dispatch but for some reason when I evaluate the condition with isReady I'm always told it isn't. The code:
App.js:
import React from 'react'
import { NavigationContainer } from '#react-navigation/native'
import 'react-native-gesture-handler'
// Provider
import { Provider as AuthProvider } from './src/context/AuthContext'
// Navigation
import { navigationRef } from './src/navigation/NavRef'
// Screens
import ResolveAuthScreen from './src/screens/ResolveAuthScreen'
const App = () => {
return (
<AuthProvider>
<NavigationContainer ref={navigationRef}>
<ResolveAuthScreen />
</NavigationContainer>
</AuthProvider>
)
}
export default App
ResolveAuthScreen.js:
import React, { useEffect, useContext } from 'react'
// Context
import { Context as AuthContext } from '../context/AuthContext'
const ResolveAuthScreen = () => {
const { tryLocalSignIn } = useContext(AuthContext)
useEffect(() => {
tryLocalSignIn()
}, [])
return null
}
export default ResolveAuthScreen
AuthContext.js (stripped down):
import AsyncStorage from '#react-native-async-storage/async-storage'
// Context
import createContext from './createContext'
// Nav
import * as NavRef from '../navigation/NavRef'
const authReducer = (state, action) => {
switch (action.type) {
case 'signin':
return { errorMessage: '', token: action.payload }
case 'clear_error':
return { ...state, errorMessage: '' }
default:
return state
}
}
const tryLocalSignIn = dispatch => async () => {
const token = await AsyncStorage.getItem('token')
console.log({ token }) // renders token
if (token) {
dispatch({ type: 'signin', payload: token })
NavRef.navigate('TrackListScreen')
} else {
NavRef.navigate('SignUp')
}
}
export const { Provider, Context } = createContext(
authReducer,
{ tryLocalSignIn },
{ token: null, errorMessage: '' },
)
NavRef.js:
import { createNavigationContainerRef } from '#react-navigation/native'
export const navigationRef = createNavigationContainerRef()
export function navigate(name, params) {
console.log({ name, params })
if (navigationRef.isReady()) {
console.log('ready')
console.log({ name, params })
navigationRef.navigate('TrackDetailScreen', { name, params })
} else {
console.log('not ready')
}
}
When I log the token from dispatch I get back the token. When I log the screen I get back TrackListScreen from navigate but whenever it's fired it always returns the console log of not ready.
Docs:
Navigating without the navigation prop
Navigating to a screen in a nested navigator
"dependencies": {
"#react-native-async-storage/async-storage": "~1.15.0",
"#react-navigation/bottom-tabs": "^6.0.9",
"#react-navigation/native": "^6.0.6",
"#react-navigation/native-stack": "^6.2.5",
"axios": "^0.24.0",
"expo": "~43.0.0",
"expo-status-bar": "~1.1.0",
"react": "17.0.1",
"react-dom": "17.0.1",
"react-native": "0.64.2",
"react-native-elements": "^3.4.2",
"react-native-gesture-handler": "~1.10.2",
"react-native-reanimated": "~2.2.0",
"react-native-safe-area-context": "3.3.2",
"react-native-screens": "~3.8.0",
"react-native-web": "0.17.1"
},
Why is my navigate not working after my dispatch or why does the isReady false?
I'm having the same issue. When trying to access the exported navigationRef.isReady() from a redux-saga file, it always returns false. I'm not sure this is a safe approach, nor have I properly tested this, but the following workaround seems to work for me:
App.js
import {setNavigationRef, navigationIsReady} from './NavigationService';
const navigationRef = useNavigationContainerRef();
return (
<NavigationContainer
ref={navigationRef}
onReady={() => {
setNavigationRef(navigationRef);
}}>
...
</NavigationContainer>
);
NavigationService.js
export let navigationRefCopy = undefined;
export function setNavigationRef(navigationRef) {
navigationRefCopy = navigationRef;
}
export function navigationIsReady() {
return navigationRefCopy?.isReady(); // returns true when called in a redux saga file.
}

react-redux-firebase Firestore not ever loading the data

I have a react-native app that is using react-redux-firebase, react-native-firebase to connect to a firestore to return some todo records. There are records in the store, however all that is displayed is the "loading" text. It never updates the todos.
I have tried connecting directly within the app and it works fine so I'm assuming that firebase is configured correctly.
this.ref.get().then((querySnapshot) => {
console.log("ListItemFS: started query snapshot", querySnapshot);
this.handleSnapshot(querySnapshot);
});
This is my App.tsx
import React from 'react'
import { Provider } from 'react-redux'
import { ReactReduxFirebaseProvider } from 'react-redux-firebase';
import firebase from 'react-native-firebase'
import { createStore, compose } from 'redux'
import { composeWithDevTools } from "redux-devtools-extension";
import { createFirestoreInstance, firestoreReducer } from 'redux-firestore'
import { combineReducers } from 'redux';
import { firebaseStateReducer } from 'react-redux-firebase'
import Todos from './Todos';
//Configure firebase
const fbConfig = {
userProfile: 'users',
useFirestoreForProfile: true, // Firestore for Profile instead of Realtime DB,
enableLogging: true
}
//firebase.initializeApp(fbConfig) // <- I have tried with and without this with no difference.
//let db = firebase.firestore(); makes no difference
//Configure reducers
const rootReducer = combineReducers({
firebase:firebaseStateReducer,
firestore: firestoreReducer
})
// Configure store
const store = createStore(rootReducer, compose(composeWithDevTools()))
export default () => (
<Provider store={store}>
<ReactReduxFirebaseProvider
firebase={firebase}
config={fbConfig}
dispatch={store.dispatch}
createFirestoreInstance={createFirestoreInstance}
>
<Todos />
</ReactReduxFirebaseProvider>
</Provider>
)
This is my TODO component
import React from 'react'
import { connect } from 'react-redux'
import { compose } from 'redux'
import { isLoaded, isEmpty } from 'react-redux-firebase/lib/helpers'
import { View ,Text} from 'react-native'
import { firestoreConnect } from 'react-redux-firebase'
function Todos({ todos, firebase, state }) {
console.log("ToDos",todos);
console.log("state",state);
if (!isLoaded(todos)) {
return <Text>Loading...</Text>
}
if (isEmpty(todos)) {
return <Text>Todos List Is Empty</Text>
}
return (
<View>
{
Object.keys(todos).map(
(key, id) => (
<Text >First Key:{todos[key]}</Text>
)
)
}
</View>
)
}
export default compose(
firestoreConnect(() => [
{ collection: 'todos' } //'todos' // { path: '/todos' } // neither works
]),
connect(state => ({
todos: state.firestore.data.todos
// profile: state.firebase.profile // load profile
}))
)(Todos)
The following are my dependencies
"dependencies": {
"expo": "^34.0.1",
"fbjs": "^1.0.0",
"react": "16.8.3",
"react-dom": "16.8.3",
"react-native": "0.59.10",
"react-native-firebase": "5.2.0",
"react-native-gesture-handler": "~1.3.0",
"react-native-reanimated": "~1.1.0",
"react-native-router-flux": "^4.1.0-beta.8",
"react-native-screens": "1.0.0-alpha.22",
"react-native-size-matters": "^0.2.1",
"react-native-unimodules": "~0.5.2",
"react-native-web": "^0.11.4",
"react-navigation-deprecated-tab-navigator": "^1.3.0",
"react-redux": "^5.0.6",
"react-redux-firebase": "^3.0.0-beta.2",
"recompose": "^0.30.0",
"redux": "3.x.x",
"redux-devtools-extension": "^2.13.8",
"redux-firestore": "^0.8.0"
}
If you have any suggestions on what I've done wrong I would be really grateful.
Try to add
import 'react-native-firebase/firestore'
in your App.tsx

connect doesn't refresh when state change on react-native with redux

It's been 5 hours that I'm on the problem but decidedly it does not want to work...
I would like to dispatch the event when onScroll is detected on my home component and receive the status "true" or "false" in my TopNavigation component
For now my reducer works well (with a console.log(nextState) before the render) but I have the impression that the connection does not work with connect(mapStatetoProps)(TopNavigation) because my component does not re-render
Thank you for your answers
//TopNavigation
import React from 'react'
import { connect } from 'react-redux'
class TopNavigation extends React.Component {
constructor(props) {
super(props)
}
componentDidMount() {
console.log(this.props.scrollData)
}
}
// Render things...
const mapStatetoProps = (state) => {
return {
scrollData: state.scrollData
}
}
export default connect(mapStatetoProps)(TopNavigation)
// Home
import React from 'react'
import { StyleSheet, View, FlatList } from 'react-native'
import gStyles from '../../../Styles/global'
import { connect } from 'react-redux'
// Partials
import ItemBox from '../../Partials/ItemBox'
import TopNavigation from '../../Partials/TopNavigation'
// Data
import recetteData from '../../../api/recetteData'
class Home extends React.Component {
constructor(props) {
super(props)
}
render() {
return (
<View style={styles.mainContainer}>
<FlatList
data={recetteData}
keyExtractor={(item) => item.id.toString()}
onPress={() => this._toggleSet()}
renderItem={({ item }) => <ItemBox item={item} />}
onScroll={(event) => this.props.dispatch({ type: "POSITION", value: event.nativeEvent.contentOffset.y })}
style={styles.flatListContainer} />
<TopNavigation />
</View>
)
}
}
export default connect(mapStateToProps)(Home)
//ScrollData Reducer
const initialState = {
scrollData: {
scrolled: false
}
}
function scrollData(state = initialState, action) {
let nextState
switch (action.type) {
case 'POSITION':
if (action.value > 0) {
nextState = {
...state,
scrollData: {
...state.scrollData,
scrolled: true,
},
}
}
else {
nextState = {
...state,
scrollData: {
...state.scrollData,
scrolled: false
},
}
}
return nextState.scrollData.scrolled
default:
return state
}
}
export default scrollData
//ConfigureStore
import { createStore } from 'redux';
import buttonPreference from './Reducers/buttonPreference'
import scrollData from './Reducers/scrollData'
export default createStore(/*buttonPreference,*/scrollData)
On console (console.log of componentDidMount of TopNavigation):
Object {
"scrolled": false,
}
But no change when i'm scrolling
Here is my package.json
{
"main": "node_modules/expo/AppEntry.js",
"scripts": {
"start": "expo start",
"android": "expo start --android",
"ios": "expo start --ios",
"eject": "expo eject"
},
"dependencies": {
"expo": "^32.0.6",
"react": "^16.8.3",
"react-native": "https://github.com/expo/react-native/archive/sdk-32.0.1.tar.gz",
"react-native-elevated-view": "0.0.6",
"react-native-gesture-handler": "^1.1.0",
"react-native-paper": "^2.12.0",
"react-native-responsive-dimensions": "^2.0.1",
"react-navigation": "^2.0.1",
"react-navigation-material-bottom-tabs": "^0.4.0",
"react-redux": "^6.0.1",
"redux": "^4.0.1"
},
"devDependencies": {
"babel-preset-expo": "^5.0.0",
"react-test-renderer": "^16.6.0-alpha.8af6728",
"schedule": "^0.4.0"
},
"private": true
}
Update
Putting on TopNavigation:
//TopNavigation
constructor(props) {
super(props)
this.state = {
scrolledState: false
}
}
componentDidUpdate(prevProps) { // Instead of componentDidMount
if (this.props.scrollData.scrolled !== prevProps.scrollData.scrolled) {
console.log(this.props.scrollData);
this.setState({ scrolledState: this.props.scrollData });
}
}
But it still doesn't work, no event or state change...
Update 2
The store seems to be work oroperly, the problem more precisely is that it does not update in real time the component.
If I populate the store, I quite and return to the page using navigation, the data is well changed.
The real question is, why the component does not update in real time with the new store data passed by the reducer...
Update 3
Expo in production mode solved problem...
You have done everything right for the most part. The problem is with your TopNavigation file. Two important things to keep in mind here:
componentDidMount() is called only once, when your component is rendered for the first time. So even if your connect works correctly, you will not get more than one call to this function. To check if your props are updated correctly, you can have a console.log() inside componentDidUpdate() as follows:
componentDidUpdate(prevProps) {
if (this.props.scrollData.scrolled !== prevProps.scrollData.scrolled) {
console.log(this.props.scrollData);
}
}
Also keep in mind that this will not cause a re-render of your component. A component re-renders only when the state of the component changes. You can use this change in your props to trigger a state change, which will call the render function again, to trigger a re-render of your component, as follows:
state = {scrolledState: false};
...
...
componentDidUpdate(prevProps) {
if (this.props.scrollData.scrolled !== prevProps.scrollData.scrolled) {
// console.log(this.props.scrollData);
this.setState({scrolledState: this.props.scrollData});
}
}
Hope this helps!
That sounds like a bug with environnement. I launched Expo in production mode and it solved problem.
On folder .expo
//setting.json
{
"hostType": "lan",
"lanType": "ip",
"dev": false, // false for production env
"minify": false,
"urlRandomness": "53-g5j"
}
I hope it can help but it would be desirable to be able to continue working on dev mode...
I report a bug on expo github
The previous answer was right. But to make the code works try the below approach.
class Home extends React.Component {
constructor(props) {
super(props);
this.state = {
scrolling: false;
}
}
makeScroll = (event) => {
this.props.dispatch({ type: "POSITION", value: event.nativeEvent.contentOffset.y
});
setTimeout(() => this.setState({scrolling: true}), 150);
}
render() {
return (
<View style={styles.mainContainer}>
<FlatList
data={recetteData}
keyExtractor={(item) => item.id.toString()}
onPress={() => this._toggleSet()}
renderItem={({ item }) => <ItemBox item={item} />}
onScroll={(event) => this.makeScroll(event)}
style={styles.flatListContainer} />
<TopNavigation />
</View>
)
}
}
export default connect(mapStateToProps)(Home)
Instead of directly dispatch at onScroll event. Pass it into a function and do change the local state inside that after dispatch.

`push` from "connected-react-router" used in redux-observable epic doesn't change the url and renders empty page

push from "connected-react-router" used in redux-observable epic doesn't change the url and renders empty page. state.router.location never changes, so I think that the action does not get dispatched properly, but the components are not rendered any more - that's a change I can't figure out.
The app is as follows:
In reducers:
const rootReducer: Reducer<any, any> = history => combineReducers({
router: connectRouter(history),
})
In app config:
const history = createBrowserHistory({
basename: ROOT_PATH,
})
<Provider store={store}>
<ConnectedRouter history={history}>
<RootContainer />
</ConnectedRouter>
</Provider>
const configureStore = (): Store => {
return createStore(
rootReducer(history),
applyMiddleware(createEpicMiddleware(rootEpic)),
)
}
In RootContainer.js
import { withRouter } from "react-router-dom"
const Root = withRouter(connect(mapStateToProps, mapDispatchToProps)(RootComponent))
export default Root
In epics:
import { push } from "connected-react-router"
const navigateTo = (action$: ActionsObservable<Action>): ActionsObservable<Action> => (
action$.pipe(
ofType(SharedActions.OPEN_WINDOW),
mergeMap((action) => {
return of(push(action.payload.url))
}),
)
)
package.json
"connected-react-router": "^5.0.1",
"react": "^16.2.0",
"react-router": "^4.2.0",
"react-router-dom": "^4.2.2",
"redux": "^3.6.0",
"redux-observable": "^0.17.0",
"rxjs": "^5.5.7",
I don't have any hot module replacement like in this example, but I think it's not related.
UPDATE:
I've added epics to listen for:
import { CALL_HISTORY_METHOD, LOCATION_CHANGE, push } from "connected-react-router"
It seems that the following action gets dispatched:
{
type: "##router/CALL_HISTORY_METHOD"
payload: {
args: [ "new/path" ]
method: "push"
}
}
​
​It just doesn't have any effect on the url.
UPDATE
Also using Link (react-router-dom) to directly navigate to the "new/path" works great inside components, so the path is correct.
It looks like you may have forgotten to add the routerMiddleware when creating your Redux store:
https://github.com/supasate/connected-react-router#step-2
Your app config above should be:
import { routerMiddleware } from "connected-react-router";
const history = createBrowserHistory({
basename: ROOT_PATH,
})
<Provider store={store}>
<ConnectedRouter history={history}>
<RootContainer />
</ConnectedRouter>
</Provider>
const configureStore = (): Store => {
return createStore(
rootReducer(history),
applyMiddleware(
createEpicMiddleware(rootEpic),
routerMiddleware(history),
),
)
}

How to call a graphql query again with different variable value?

This is my code file.
import React from 'react'
import { gql } from 'apollo-boost'
import { Query } from 'react-apollo'
const CATEGORY_QUERY = gql`Some query here`
const handleOnCategoryTouch = (id) => {
//Again call the query
}
const Home = () => (
<Query
query={CATEGORY_QUERY}
variables={{ limit: 20, cursor: "", idcategory: 0 }}
>
{({ loading, error, data }) => {
if (loading) return <Text>Loading...</Text>;
if (error) return <Text>Error :(</Text>;
return (
<Categories
categories={data.get_discovery_kol_data.categories}
content={data.get_discovery_kol_data.postKol}
onCategoryTouch={handleOnCategoryTouch}
/>
)
}}
</Query>
Here Categories is a component which renders categories as a chips in UI. On click of that chip, we get the categoryId. Now I want to recall the CATEGORY_QUERY, with the latest id and update the existing result, how do it ?
I am using
"apollo-boost": "^0.1.3",
"graphql": "^0.13.2",
"react": "^16.3.0-alpha.1",
"react-apollo": "^2.1.0-beta.3",
"react-native": "0.54.2",
Best approach would be to create wrapping component
parent.js
import Home from './Home'
class Parent extends React.Component {
render() {
return <Home
category={this.state.id}
onCategoryChanged={(id) => { this.onCategoryChanged(id) }}
/>
}
onCategoryChanged (id) {
this.setState({ id })
}
}
Home.js
class Home extends React.Component {
render() {
return (
<a onClick={() => { this.props.onCategoryChanged(1) }}>Change category to 1</a>
)
}
}
export default graphql(CATEGORY_QUERY, {
options: ({ category }) => ({
variables: { category }
})
})(Home)
This will force parent component to re-render Home with new props and call query again with new variable for categoryId