I am trying to load my parent component from child component on button press. But it's not rendering the parent components from btnPress method. I am not getting any error.
onButtonPress
<Button onPress={() => btnPress(parent_id, id)}>
<Icon name="arrow-forward" />
</Button>
btnPress Function
function btnPress(parent_id, id) {
const App = () => (
//I have tried this way but this didn't work. No any error, i can see log on console
<Container>
<Headerc headerText={'Fitness sdaf'} />
<ExerciseList pId={parent_id} mId={id} />
</Container>
);
console.log(id);
AppRegistry.registerComponent('weightTraining', () => App);
}
full code(child component)
import React from 'react';
import { Right, Body, Thumbnail, Container, ListItem, Text, Icon } from 'native-base';
import { AppRegistry
} from 'react-native';
import Headerc from './headerc';
import ExerciseList from './exerciseList';
import Button from './Button';
const ExerciseDetail = ({ exercise }) => {
const { menu_name, menu_icon, parent_id, id } = exercise;
function NumberDescriber() {
let description;
if (menu_icon === 'noimg.jpg') {
description = `http://www.xxxxxx.com/uploads/icons/${menu_icon}`;
} else if (menu_icon === 'noimg.jpg') {
description = menu_icon;
} else {
description = `http://www.xxxxx.com/uploads/icons/${menu_icon}`;
}
return description;
}
function btnPress(parent_id, id) {
const App = () => (
<Container>
<Headerc headerText={'Fitness sdaf'} />
<ExerciseList pId={parent_id} mId={id} />
</Container>
);
console.log('-------------------------------');
console.log(id);
console.log('+++++++++++++++++++++++++++');
AppRegistry.registerComponent('weightTraining', () => App);
}
return (
<ListItem>
<Thumbnail square size={80} source={{ uri: NumberDescriber() }} />
<Body>
<Text>{menu_name}</Text>
<Text note> {menu_name} exercise lists</Text>
</Body>
<Right>
<Button onPress={() => btnPress(parent_id, id)}>
<Icon name="arrow-forward" />
</Button>
</Right>
</ListItem>
);
};
export default ExerciseDetail;
Please do let me know, if you need more information.
I would not suggest doing that way, it look totally anti-pattern and not.
better try with navigation or create a pattern like this
inside your index.js or index.android.js or index.ios.js
import App from './App' //your app level file
AppRegistry.registerComponent('weightTraining', () => App);
now in your app js file
export default App class extends React.Component{
constructor(props){
super(props);
this.state ={
component1:false,
component2:true,
}
}
btnPressed =()=>{
//handle state update logic here
}
render(){
if(this.state.component1) return <Component1/>
return <Component2/>
}
}
**** not the best solution available, play around and you will get best
To navigate from this component to your parent component unless you want to implement your own navigation which isn't recommended, you should look into one that's already built and adopted by many in the react-native ecosystem.
Some of the biggest ones:
React Native Navigation
React Navigation
React Native Router
I personally highly recommend option number 1, since it seems to be the most production tested and production ready implementation out there
Related
I have a Dark Mode component which is a simple toggle between Sun & Moon icons.
DarkMode.tsx
import { observer } from 'mobx-react'
import { MoonIcon, SunIcon } from '#heroicons/react/solid'
import { useStore } from '#/store/index'
export const DarkMode = observer(() => {
const { theme, setTheme, isPersisting } = useStore()
if (!isPersisting) return null
return (
<>
{theme === 'dark' && (
<button
className="fixed bottom-12 right-12 focus:outline-none"
title="Activate light mode"
onClick={() => {
setTheme('light')
}}
>
<MoonIcon className="w-8 h-8" />
</button>
)}
{theme === 'light' && (
<button
className="fixed bottom-12 right-12 focus:outline-none"
title="Activate dark mode"
onClick={() => {
setTheme('dark')
}}
>
<SunIcon className="w-8 h-8" />
</button>
)}
</>
)
})
I am using MobX to track my theme & mobx-persist-store to persist the data in localStorage.
store.ts
import { makeObservable, observable, action } from 'mobx'
import { makePersistable, isPersisting, clearPersistedStore } from 'mobx-persist-store'
import type { Theme, IStore } from '#/types/index'
const name = 'Store'
const IS_SERVER = typeof window === 'undefined'
export class Store implements IStore {
theme: Theme = 'light'
constructor() {
makeObservable(this, {
theme: observable,
setTheme: action.bound,
reset: action.bound,
})
if (!IS_SERVER) {
makePersistable(this, { name, properties: ['theme'], storage: window.localStorage })
}
}
setTheme(theme: Theme) {
this.theme = theme
}
get isPersisting() {
return isPersisting(this)
}
async reset() {
if (!IS_SERVER) await clearPersistedStore(this)
}
}
And I am adding dark class to html when the user selectes dark theme in Dark Mode component.
_app.tsx
import React from 'react'
import { AppProps } from 'next/app'
import Head from 'next/head'
import { observer } from 'mobx-react'
import useSystemTheme from 'use-system-theme'
import { useStore } from '#/store/index'
import '#/components/NProgress'
import 'nprogress/nprogress.css'
import '#/styles/index.css'
const MyApp = ({ Component, pageProps }: AppProps) => {
const systemTheme = useSystemTheme()
const { theme, setTheme } = useStore()
React.useEffect(() => {
const isDarkTheme = theme === 'dark' || (systemTheme === 'dark' && theme !== 'light')
if (isDarkTheme) {
document.documentElement.classList.add('dark')
setTheme('dark')
} else {
document.documentElement.classList.remove('dark')
setTheme('light')
}
}, [theme, systemTheme])
return (
<>
<Component {...pageProps} />
</>
)
}
export default observer(MyApp)
I am still getting an error that says:
VM356 main.js:16820 Warning: Expected server HTML to contain a matching <button> in <div>.
at button
at wrappedComponent (http://localhost:3000/_next/static/chunks/pages/_app.js?ts=1624277701361:2690:73)
at Nav (http://localhost:3000/_next/static/chunks/pages/tutorial/the-complete-guide-to-starting-a-blog-in-nextjs-and-mdx.js?ts=1624277701361:12454:23)
at Tutorial (http://localhost:3000/_next/static/chunks/pages/tutorial/the-complete-guide-to-starting-a-blog-in-nextjs-and-mdx.js?ts=1624277701361:12973:24)
at MDXLayout
at http://localhost:3000/_next/static/chunks/pages/tutorial/the-complete-guide-to-starting-a-blog-in-nextjs-and-mdx.js?ts=1624277701361:7880:30
at MDXContent (http://localhost:3000/_next/static/chunks/pages/tutorial/the-complete-guide-to-starting-a-blog-in-nextjs-and-mdx.js?ts=1624277701361:22563:25)
at wrappedComponent (http://localhost:3000/_next/static/chunks/pages/_app.js?ts=1624277701361:2690:73)
at ErrorBoundary (http://localhost:3000/_next/static/chunks/main.js?ts=1624277701361:767:47)
at ReactDevOverlay (http://localhost:3000/_next/static/chunks/main.js?ts=1624277701361:883:23)
at Container (http://localhost:3000/_next/static/chunks/main.js?ts=1624277701361:8756:5)
at AppContainer (http://localhost:3000/_next/static/chunks/main.js?ts=1624277701361:9244:24)
at Root (http://localhost:3000/_next/static/chunks/main.js?ts=1624277701361:9380:25)
The button's onClick event handler disappears from the DOM itself.
Funny thing is it used to work on MacOS but not on Windows. I cloned the same project. What's the issue?
I has the same issue during dark/light theme implementation, but solved it based on deadcoder0904's comment.
In my case, the app was built using next-themes library.
As this error is related to SSR, it's needed to confirm the component is mounted on the frontend side.
import { useEffect, useState } from 'react';
import { useTheme } from 'next-themes';
const ThemeToggler = () => {
const { theme, setTheme } = useTheme()
const [hasMounted, setHasMounted] = useState(false);
useEffect(() => setHasMounted(true));
// this line is the key to avoid the error.
if (!hasMounted) return null;
return (
<div>
The current theme is: {theme}
<button onClick={() => setTheme('light')}>Light Mode</button>
<button onClick={() => setTheme('dark')}>Dark Mode</button>
</div>
)
}
export default ThemeToggler;
Hope this helps you.
On the server your DarkMode component does not render anything (because isPersisting is false). And then on the client it renders something on the first pass (isPersisting becomes true on the client render) and that is why React (not Next.js) complains that markup between SSR and CSR does not match.
Basically it means that you always need to render some theme with SSR, but SSR does not know about localStorage so it can only pick the default value. And then correct value will be picked from localStorage after client render.
If you want to render correct theme with SSR without flashing of old theme or without errors like that one then you need to store it in cookies.
The missing piece of the puzzle was I had wrapped my Nav outside of ThemeProvider.
Nav contained DarkMode so it couldn't access ThemeProvider. My _document.tsx looked like:
<Nav />
<ThemeProvider attribute="class" themes={['light', 'dark']}>
<Component {...pageProps} />
</ThemeProvider>
So I had to bring that Nav inside ThemeProvider to get it working.
<ThemeProvider attribute="class" themes={['light', 'dark']}>
<Nav />
<Component {...pageProps} />
</ThemeProvider>
The following is my use-case:
There is an existing web app (say https://example.com). It has some functions in modules that are called on load of the page. For example:
// index.js
import * as test from "./test.js";
The test.js has its functions
// test.js
const check = () => {
alert("check called");
}
export { check }
Now from the React Native side I wish to call the check function using injectJavascript. The App.js looks like this:
import React, { Component } from 'react';
import { View, Text, Button } from 'react-native';
import { WebView } from 'react-native-webview';
class App extends Component {
constructor(props) {
super(props)
this.webviewRef = null;
}
render() {
return (
<>
<View style={{ flex: 1, alignContent: 'center', justifyContent: 'center' }}>
<Button
title="Check"
onPress={() => {
const clientResponseCode = `
test.check();
true;
`;
this.webviewRef.injectJavaScript(clientResponseCode);
}}
/>
</View>
<WebView
source={{ uri: 'https://example.com' }}
ref={ref => this.webviewRef = ref}
javaScriptEnabled={true}
onMessage={this.onMessage}
/>
</>
);
}
}
//make this component available to the app
export default App;
What works inside clientResponseCode:
document.querySelector("#any-id").innerHTML = "....";
What does not work inside clientResponseCode:
test.check();
document.test.check();
window.test.check();
I also tried to to use the postMessage method with addEventListener on the web side but it does not get invoked. I tried both these below options.
window.postMessage("hello");
windows.ReactNativeWebView.postMessage("hello");
What will be the way to invoke the check method from the test module that is imported in index.js on the website?
Or is it that only the DOM is available to App.js and only dynamically loading of javascript code is permitted? If so, then I will create some webcomponent to inject the dynamic passing of objects (blob, in my case) to the DOM.
Thanks for the help
The following seemed to have worked.
In index.js on the web side
//index.js
..
..
// add the following
window.check = test.check; // without brackets
...
on the React App side
// App.js
...
...
const clientResponseCode = `
window.check();
true;
`;
this.webviewRef.injectJavaScript(clientResponseCode);
It is possible to pass parameters too to the check function
Hi Please see my code Here :
https://snack.expo.io/#ersimransingh/navigation-problem
I have created my switch navigator to navigate between pages from App.js to Second.js
On App.js page I have imported Second.js file module named App1 Which works file.
Moreover, I did the same thing on Second.js file imported App component from App.js But swith navigator show error on the page says
The component for route 'App' must be a React component.
I did search on internet for the same, and tried replacing my imporing syntax from
import {App} from './App';
to
import App from './App';
You can Check my code on expo
https://snack.expo.io/#ersimransingh/navigation-problem
You have App.js which is creating a route using Second.js and Second.js is creating a route using App.js. This is definitely problematic, because you are creating a cicular reference. Rather, you should create your navigation in one place and use it in the App.js
Here is an example:
App.js
export default class App extends React.Component{
render(){
return(
<CreateTag />
);
}
}
const AppContainer = createSwitchNavigator({
FirstScreen,
SecondScreen
});
const CreateTag = createAppContainer(AppContainer);
FirstScreen.js
export default class FirstScreen extends React.Component {
render() {
let { navigation } = this.props;
return (
<View>
<Text
style={styles.sampleText}
onPress={() => navigation.navigate('SecondScreen')}>
First screen
</Text>
</View>
);
}
}
SecondScreen.js
export default class SecondScreen extends React.Component {
render() {
let { navigation } = this.props;
return (
<View>
<Text
style={styles.sampleText}
onPress={() => navigation.navigate('FirstScreen')}>
Second screen
</Text>
</View>
);
}
}
Here is the complete example: https://snack.expo.io/S1cY9IVEV
Also you can check from the official example: https://github.com/react-navigation/react-navigation/blob/master/examples/NavigationPlayground/js/App.js
I hope this helps.
I'm building a React Native app. I have imported createStackNavigator from react-navigation. I'm able to get it working on my Home screen - I click a button, it brings me to a new component. This is the code that I'm using to bring it into my Home.js
// src/components/Home/Home
export class Home extends Component {
render() {
return (
<React.Fragment>
<Button
title="Test button"
onPress={() => this.props.navigation.navigate('Roads')}
/>
<StatusBar />
<Header />
<Menu />
</React.Fragment>
);
}
}
const RootStack = createStackNavigator(
{
Home: Home,
Roads: Roads,
},
{
initialRouteName: 'Home',
}
);
export default class App extends React.Component {
render() {
return <RootStack />;
}
}
My Home page takes in a Menu which has a list of MenuItems. I am trying to get the MenuItems to jump to the appropriate pages. When I try to bring in the navigation inside MenuItem.js's render method, like so:
// src/components/Roads/Roads
render() {
const { navigate } = this.props.navigation;
console.log(this.props, "props is here");
I get the following error message:
TypeError: undefined is not an object (evaluating 'this.props.navigation.navigate').
Do I need to pass the navigator down in props to Menu.js and then to MenuItem.js? The docs give examples but it seems to be examples that assume you jam all your code into one file rather than across several components.
Have I set this up correctly?
When using a Navigator from react-navigation only the components you declare as Screens inherit the navigation prop (in your case Home and Roads)
This means that you will need to pass it as a prop to its children as you said:
<Menu navigation={this.props.navigation} />
<MenuItem navigation={this.props.navigation} />
In case anyone is wondering how to navigate from a component that isn't inside a Navigator then I suggest reading this part of the react-navigation documentation
https://reactnavigation.org/docs/en/navigating-without-navigation-prop.html
I have written a Higher Order Component:
import React from 'react';
const NewHOC = (PassedComponent) => {
return class extends React.Component {
render(){
return (
<div>
<PassedComponent {...this.props}/>
</div>
)
}
}
}
export default NewHOC;
I am using the above in my App.js:
import React from 'react';
import Movie from './movie/Movie';
import MyHOC from './hoc/MyHOC';
import NewHOC from './hoc/NewHOC';
export default class App extends React.Component {
render() {
return (
<div>
Hello From React!!
<NewHOC>
<Movie name="Blade Runner"></Movie>
</NewHOC>
</div>
);
}
}
But, the warning I am getting is:
Warning: Functions are not valid as a React child. This may happen if
you return a Component instead of <Component /> from render. Or maybe
you meant to call this function rather than return it.
in NewHOC (created by App)
in div (created by App)
in App
The Movie.js file is:
import React from "react";
export default class Movie extends React.Component{
render() {
return <div>
Hello from Movie {this.props.name}
{this.props.children}</div>
}
}
What am I doing wrong?
I did encounter this error too because I didn't use the correct snytax at routing. This was in my App.js under the <Routes> section:
False:
<Route path="/movies/list" exact element={ MoviesList } />
Correct:
<Route path="/movies/list" exact element={ <MoviesList/> } />
So now the MoviesList is recognized as a component.
You are using it as a regular component, but it's actually a function that returns a component.
Try doing something like this:
const NewComponent = NewHOC(Movie)
And you will use it like this:
<NewComponent someProp="someValue" />
Here is a running example:
const NewHOC = (PassedComponent) => {
return class extends React.Component {
render() {
return (
<div>
<PassedComponent {...this.props} />
</div>
)
}
}
}
const Movie = ({name}) => <div>{name}</div>
const NewComponent = NewHOC(Movie);
function App() {
return (
<div>
<NewComponent name="Kill Bill" />
</div>
);
}
const rootElement = document.getElementById("root");
ReactDOM.render(<App />, rootElement);
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react-dom.min.js"></script>
<div id="root"/>
So basically NewHOC is just a function that accepts a component and returns a new component that renders the component passed in. We usually use this pattern to enhance components and share logic or data.
You can read about HOCS in the docs and I also recommend reading about the difference between react elements and components
I wrote an article about the different ways and patterns of sharing logic in react.
In my case i forgot to add the () after the function name inside the render function of a react component
public render() {
let ctrl = (
<>
<div className="aaa">
{this.renderView}
</div>
</>
);
return ctrl;
};
private renderView() : JSX.Element {
// some html
};
Changing the render method, as it states in the error message to
<div className="aaa">
{this.renderView()}
</div>
fixed the problem
I encountered this error while following the instructions here: https://reactjs.org/docs/add-react-to-a-website.html
Here is what I had:
ReactDOM.render(Header, headerContainer);
It should be:
ReactDOM.render(React.createElement(Header), headerContainer);
I had this error too. The problem was how to call the function.
Wrong Code:
const Component = () => {
const id = ({match}) => <h2>Test1: {match.params.id}</h2>
return <h1>{id}</h1>;
};
Whereas id is a function, So:
Correct code:
return <h1>{id()}</h1>;
Adding to sagiv's answer, we should create the parent component in such a way that it can consist all children components rather than returning the child components in the way you were trying to return.
Try to intentiate the parent component and pass the props inside it so that all children can use it like below
const NewComponent = NewHOC(Movie);
Here NewHOC is the parent component and all its child are going to use movie as props.
But any way, you guyd6 have solved a problem for new react developers as this might be a problem that can come too and here is where they can find the solution for that.
I was able to resolve this by using my calling my high order component before exporting the class component. My problem was specifically using react-i18next and its withTranslation method, but here was the solution:
export default withTranslation()(Header);
And then I was able to call the class Component as originally I had hoped:
<Header someProp={someValue} />
it also happens when you call a function from jsx directly rather than in an event. like
it will show the error if you write like
<h1>{this.myFunc}<h2>
it will go if you write:
<h1 onClick={this.myFunc}>Hit Me</h1>
I was getting this from webpack lazy loading like this
import Loader from 'some-loader-component';
const WishlistPageComponent = loadable(() => import(/* webpackChunkName: 'WishlistPage' */'../components/WishlistView/WishlistPage'), {
fallback: Loader, // warning
});
render() {
return <WishlistPageComponent />;
}
// changed to this then it's suddenly fine
const WishlistPageComponent = loadable(() => import(/* webpackChunkName: 'WishlistPage' */'../components/WishlistView/WishlistPage'), {
fallback: '', // all good
});
In my case, I was transport class component from parent and use it inside as a prop var, using typescript and Formik, and run well like this:
Parent 1
import Parent2 from './../components/Parent2/parent2'
import Parent3 from './../components/Parent3/parent3'
export default class Parent1 extends React.Component {
render(){
<React.Fragment>
<Parent2 componentToFormik={Parent3} />
</React.Fragment>
}
}
Parent 2
export default class Parent2 extends React.Component{
render(){
const { componentToFormik } = this.props
return(
<Formik
render={(formikProps) => {
return(
<React.fragment>
{(new componentToFormik(formikProps)).render()}
</React.fragment>
)
}}
/>
)
}
}
What would be wrong with doing;
<div className="" key={index}>
{i.title}
</div>
[/*Use IIFE */]
{(function () {
if (child.children && child.children.length !== 0) {
let menu = createMenu(child.children);
console.log("nested menu", menu);
return menu;
}
})()}
In my case I forgot to remove this part '() =>'. Stupid ctrl+c+v mistake.
const Account = () => ({ name }) => {
So it should be like this:
const Account = ({ name }) => {
In my case
<Link key={uuid()} to="#" className="tag">
{post.department_name.toString}
</Link>
changed with
<Link key={uuid()} to="#" className="tag">
{post.department_name.toString()}
</Link>
You should use
const FunctionName = function (){
return (
`<div>
hello world
<div/>
`
)
};
if you use Es6 shorthand function it will give error use regular old javascript function.