vue3, How to use dynamic component in tsx - vue.js

Failed to resolve component: component
If this is a native custom element, make sure to exclude it from component resolution via compilerOptions.isCustomElement.
<script lang="tsx">
export default defineComponent({
setup(props) {
return () => (
<n-form>
{list.value.map(item => {
return (
<n-form-item>
<component is="*****" ></component>
</n-form-item>
);
})}
</n-form>
);
}
})
</script>

<component is="*****" ></component> will not work using JSX.
instead you can just pass the component itself
import Comp1 from "./Comp1.vue";
export default defineComponent({
setup(props) {
return () => (
<n-form>
{list.value.map(item => {
const MyComponent = Comp1;
return (
<n-form-item>
<MyComponent/>
</n-form-item>
);
})}
</n-form>
);
}
})
of course, using const MyComponent = Comp1; is not exactly dynamic, but you can replace that with a lookup like
import Comp1 from "./Comp1.vue";
import Comp2 from "./Comp2.vue";
import Comp3 from "./Comp3.vue";
cons componentDefs = {
"comp-1": Comp1,
"comp-2": Comp2,
"comp-3": Comp3,
}
and then you can look them up with
const MyComponent = componentDefs[*******];

Related

Prevent Child Rerendering if Parent is Rerendered Using Hooks

My bestSellerDummy data doesn't change, so I'd like to prevent the same Product child to be rerendered if parent rerenders. I have tried using useMemo in parent and React.memo in child but no luck, it's still showing log 'Rendering Product component..' every time parent rerenders. What am I missing here? Please advice.
Note: Parent is expected to be rerendered every time I call addToCart function (of CartContext) in a Product component.
I'm using CartContext, maybe related to this, I'm not sure. Here is the sandbox: https://codesandbox.io/s/dazzling-moore-po1c6?file=/src/App.js
Home.tsx
const [bestSellerDummy] = useState(
[...new Array(5)].map((item, key) => ({
id: key,
imageUri:'https://1.jpg',
name: 'My Dummy 1',
price: 25,
})),
);
const bestSellers = useMemo(() => {
return bestSellerDummy.map((productDummy, key) => {
return (
<Product key={key} product={productDummy} />
);
});
}, [bestSellerDummy]);
return (
...
{bestSellers}
...
)
Product.tsx
const Product: FunctionComponent<IProductProps> = (
productProps,
) => {
...
console.log('Rendering Product component..');
...
}
export default React.memo(Product);
=== EDIT: MY VERSION OF ANSWER ===
Finally! After playing around with useCallback, useMemo, fast-memoize plugin.. What suits the best for me is using useReducer in Context combine with wrapping the expensive component with React.memo. I think this is the most clean and elegant way to optimize child components. Working sandbox is here: https://codesandbox.io/s/eloquent-albattani-8x7h9?file=/src/App.js
Since you are using useContext, your component will always re-renders.
When the nearest <MyContext.Provider> above the component updates, this Hook will trigger a rerender with the latest context value passed to that MyContext provider. Even if an ancestor uses React.memo or shouldComponentUpdate, a rerender will still happen starting at the component itself using useContext.
Reference: https://reactjs.org/docs/hooks-reference.html#usecontext
I was trying to refactor your code using the 2nd strategy pointed from the docs: https://github.com/facebook/react/issues/15156#issuecomment-474590693.
However, I soon realized that the addToCart function has cartItems as its dependency, so whenever cartItems changes, addToCart changes and it's kind of impossible to avoid re-renders since every Product component use addToCart function.
That leads me to the use of useReducer because React guarantees that its dispatch is stable and won't change during re-renders.
So here's the working Codesandbox: https://codesandbox.io/s/red-feather-dc7x6?file=/src/App.js:786-797
Wrap BestSellers component with React.memo too. Don't use useMemo to avoid unnecessary component updating because it may cause bugs. It is used for computing expensive values.
Source: https://reactjs.org/docs/hooks-reference.html#usememo
This is the best way to clear your concepts about useCallback, useMemo and useEffect.
App.js
import Child1 from "./Child1";
import Child2 from "./Child2";
import { useState, useEffect, useMemo, useCallback } from "react";
function App() {
const [x, setX] = useState(0);
const [y, setY] = useState(0);
console.log("Parent");
const printx = useCallback(() => {
console.log("x:" + x);
}, [x]);
useEffect(() => {
printx();
console.log("-------");
}, [printx]);
const child1 = useMemo(() => {
return <Child1 x={x} />;
}, [x]);
const child2 = useMemo(() => {
return <Child2 y={y} />;
}, [y]);
return (
<div className="App">
<h1>Parent</h1>
<button onClick={() => setX(x + 1)}>X+</button>
<button onClick={() => setY(y + 1)}>Y+</button>
{child1}
{child2}
</div>
);
}
export default App;
Child1.js
const Child1 = ({ x }) => {
console.log("Child1");
return (
<div>
<h1>Child 1:{x}</h1>
</div>
);
};
export default Child1;
Child2.js
const Child2 = ({ y }) => {
console.log("Child2");
return (
<div>
<h1>Child 2:{y}</h1>
</div>
);
};
export default Child2;
Try this way
const [bestSellerDummy, setBestSellerDummy] = useState([]); // default empty
// get data from `useCallback`
const sellerData = React.useCallback(
() => {
return [...new Array(5)].map((item, key) => ({
id: key,
imageUri:'https://1.jpg',
name: 'My Dummy 1',
price: 25,
}))
}, []
);
useEffect( () => {
setBestSellerDummy( sellerData() ); // set data when screen rendered from `useCallback`
}, [])
const bestSellers = useMemo(() => {
// ....
}, [bestSellerDummy]);
return (
// ...
{bestSellers}
// ...
)
The thing is you are using dynamic index for key . when you used dynamic key always react re render this .So use product id or some unique key for this then problem will be solved . I also have same problem and i resolved it

Change theme like Fabric Web ( Default / Dark)

In the documentation page of fabric, now each example component have a change theme funcionality
ie: example
enter image description here
How can I achive this funcionality. I have 2 themes (created in) and I want to switch betwen thems
Here is my preferred way, using React Context.
import React from 'react';
import { Fabric, Customizer } from '#fluentui/react';
import { useLocalStorage } from 'react-use';
// Setup Theme Context and create hooks
import {
DefaultCustomizations,
DarkCustomizations
} from '#uifabric/theme-samples';
export const ThemeList = {
light: DefaultCustomizations,
dark: DarkCustomizations
};
export const ThemeContext = React.createContext({
theme: 'light',
changeTheme: name => {}
});
const ThemeWrapper = ({ children }) => {
return (
<ThemeContext.Consumer>
{({ theme }) => (
<Customizer {...ThemeList[theme]}>
<Fabric>{children}</Fabric>
</Customizer>
)}
</ThemeContext.Consumer>
);
};
export const ThemeProvider = ({ children }) => {
const [theme, setTheme] = useLocalStorage('theme', 'dark');
const changeTheme = name => ThemeList[name] && setTheme(name);
const value = { theme, changeTheme };
return (
<ThemeContext.Provider value={value}>
<ThemeWrapper>{children}</ThemeWrapper>
</ThemeContext.Provider>
);
};
export const useTheme = () => React.useContext(ThemeContext);
// Now demo how to use it
export function App() {
const { theme, changeTheme } = useTheme();
return (
<button onClick={() => changeTheme('dark')}>
Switch to dark
</button>
);
}
import ReactDOM from 'react-dom';
ReactDOM.render(
<ThemeProvider>
<App />
</ThemeProvider>,
document.getElementById('root')
);
Note to moderator: Sorry that this answer was originally a duplicate. I deleted the duplicate.

Redux & React Native component connect: this.props is undefined

I am trying to access the name field as defined in the initial state of my reducer. At the moment, this.props is returning undefined.
My reducer:
import { combineReducers } from 'redux';
const INITIAL_STATE = {
name: "Test"
}
const userReducer = (state = INITIAL_STATE, action) => {
switch (action.type) {
default:
return state
}
};
export default combineReducers({
user: userReducer,
});
The component being rendered (this logs "Props: undefined"):
const AddName = ({ navigation }) => {
...
console.log("Props: ", this.props)
return (
<>
...
</>
)
}
mapStateToProps = (state) => {
return{
user : state.user
};
}
export default connect(mapStateToProps, null)(AddName)
Creating the redux store and provider:
...
import { Provider } from 'react-redux';
import { createStore } from 'redux';
import reducer from './src/reducers/userReducer';
const store = createStore(reducer)
const AppContainer = () =>
<Provider store={store} >
<App />
</Provider>
AppRegistry.registerComponent(appName, () => AppContainer);
You are using a functional component and this keyword doesnt apply to functional components, rather it applies it to class Components.
Change your component as below :
class AddName extends React.Component {
...
console.log("Props: ", this.props)
render(){
return (
<>
...
</>
)
}
}
mapStateToProps = (state) => {
return{
user : state.user
};
}
export default connect(mapStateToProps, null)(AddName)
hopeit helps. feel free for doubts
You are using a function Component so you should use props in this way :
const AddName = (props) => {
...
console.log("Props: ", props)
return (
<>
...
</>
)
}
You should use class components instead of the function component.
Read more about that.
https://codeburst.io/react-js-understanding-functional-class-components-e65d723e909

Component's prop doesn't update in React Native with Redux

I need some help with my app and Redux! (Currently, i hate it aha)
So, i have a notification page component which fetch some datas and i need to put the data length into my redux store to put badge on my icon in my tabbar!
My Main Reducer :
import { combineReducers } from "redux";
import NotificationReducer from "./NotificationReducer";
export default function getRootReducer(navReducer) {
return combineReducers({
nav: navReducer,
notificationReducer: NotificationReducer
});
}
My Notification reducer
const initialState = {
NotificationCount: 0
};
export default function notifications(state = initialState, action = {}) {
switch (action.type) {
case 'SET_COUNT' :
console.log('REDUCER NOTIFICATION SET_COUNT',state)
return {
...state,
NotificationCount: action.payload
};
default:
return state;
}
};
My Action :
export function setNotificationCount(count) {
return function (dispatch, getState) {
console.log('Action - setNotificationCount: '+count)
dispatch( {
type: 'SET_COUNT',
payload: count,
});
};
};
My Component :
import React, { Component } from 'react';
import { View, Text, StyleSheet, ScrollView, Dimensions, TouchableOpacity, SectionList, Alert } from 'react-native';
import Icon from 'react-native-vector-icons/Ionicons';
import { Notification } from '#Components';
import { ORANGE } from '#Theme/colors';
import { NotificationService } from '#Services';
import Style from './style';
import { connect } from 'react-redux';
import { bindActionCreators } from 'redux';
import * as Actions from '#Redux/Actions';
const width = Dimensions.get('window').width
const height = Dimensions.get('window').height
export class NotificationsClass extends Component {
constructor(props) {
super(props);
this.state = {
dataSource: [],
NotificationCount: undefined
};
}
async componentWillMount() {
this.updateNotifications();
}
componentWillReceiveProps(nextProps){
console.log('receive new props',nextProps);
}
async updateNotifications() {
this.props.setNotificationCount(10); <---
let data = await NotificationService.get();
if (data && data.data.length > 0) {
this.setState({ dataSource: data });
console.log(this.props) <-- NotificationCount is undefined
}
}
render() {
if (this.state.dataSource.length > 0) {
return (
<SectionList
stickySectionHeadersEnabled
refreshing
keyExtractor={(item, index) => item.notificationId}
style={Style.container}
sections={this.state.dataSource}
renderItem={({ item }) => this.renderRow(item)}
renderSectionHeader={({ section }) => this.renderSection(section)}
/>
);
} else {
return this.renderEmpty();
}
}
renderRow(data) {
return (
<TouchableOpacity activeOpacity={0.8} key={data.notificationId}>
<Notification data={data} />
</TouchableOpacity>
);
}
}
const Notifications = connect(
state => ({
NotificationCount: state.NotificationCount
}),
dispatch => bindActionCreators(Actions, dispatch)
)(NotificationsClass);
export { Notifications };
(I've removed some useless code)
Top Level :
const navReducer = (state, action) => {
const newState = AppNavigator.router.getStateForAction(action, state);
return newState || state;
};
#connect(state => ({
nav: state.nav
}))
class AppWithNavigationState extends Component {
render() {
return (
<AppNavigator
navigation={addNavigationHelpers({
dispatch: this.props.dispatch,
state: this.props.nav,
})}
/>
);
}
}
const store = getStore(navReducer);
export default function NCAP() {
return (
<Provider store={store}>
<AppWithNavigationState />
</Provider>
);
}
React : 15.6.1
React-Native : 0.46.4
Redux : 3.7.2
React-Redux : 5.0.5
React-Navigation : 1.0.0-beta.11
Node : 6.9.1
So if you've an idea! It will be great :D !
Thanks !
There's three issues.
First, React's re-rendering is almost always asynchronous. In updateNotifications(), you are calling this.props.setNotificationCount(10), but attempting to view/use the props later in that function. Even with the await in there, there's no guarantee that this.props.NotificationCount will have been updated yet.
Second, based on your reducer structure and mapState function, props.NotificationCount will actually never exist. In your getRootReducer() function, you have:
return combineReducers({
nav: navReducer,
notificationReducer: NotificationReducer
});
That means your root state will be state.nav and state.notificationReducer. But, in your mapState function, you have:
state => ({
NotificationCount: state.NotificationCount
}),
state.NotificationCount will never exist, because you didn't use that key name when you called combineReducers.
Third, your notificationReducer actually has a nested value. It's returning {NotificationCount : 0}.
So, the value you actually want is really at state.notificationReducer.NotificationCount. That means your mapState function should actually be:
state => ({
NotificationCount: state.notificationReducer.NotificationCount
}),
If your notificationReducer isn't actually going to store any other values, I'd suggest simplifying it so that it's just storing the number, not the number inside of an object. I'd also suggest removing the word Reducer from your state slice name. That way, you could reference state.notification instead.
For more info, see the Structuring Reducers - Using combineReducers section of the Redux docs, which goes into more detail on how using combineReducers defines your state shape.

Call child function from parent component in React Native

I'm developing my first React Native app. What I'm trying to achieve is to execute a child function from the parent component, this is the situation:
Child
export default class Child extends Component {
...
myfunct: function() {
console.log('Managed!');
}
...
render(){
return(
<Listview
...
/>
);
}
}
Parent
export default class Parent extends Component {
...
execChildFunct: function() {
...
//launch child function "myfunct"
...
//do other stuff
}
render(){
return(
<View>
<Button onPress={this.execChildFunct} />
<Child {...this.props} />
</View>);
}
}
In this example, I would like to log 'Managed!' when I press the button in the parent class. How is it feasible?
Nader Dabit's answer is outdated, since using String literals in ref attributes has been deprecated. This is how we would do it as of September 2017:
<Child ref={child => {this.child = child}} {...this.props} />
<Button onPress={this.child.myfunc} />
Same functionality, but instead of using a String to reference the component, we store it in a global variable instead.
Here's how you can do this with functional components:
Parent
Use useRef() to give the child component a reference in the parent:
const childRef = useRef()
// ...
return (
<ChildComponent ref={childRef} />
)
...
Child
Pass ref as one of the constructor parameters:
const ChildComponent = (props, ref) => {
// ...
}
Import useImperativeHandle and forwardRef methods from the 'react' library:
import React, { useImperativeHandle, forwardRef } from 'react'
Use useImperativeHandle to bind functions to the ref object, which will make these functions accessible to the parent
These methods won't be internally available, so you may want to use them to call internal methods.
const ChildComponent = (props, ref) => {
//...
useImperativeHandle(ref, () => ({
// each key is connected to `ref` as a method name
// they can execute code directly, or call a local method
method1: () => { localMethod1() },
method2: () => { console.log("Remote method 2 executed") }
}))
//...
// These are local methods, they are not seen by `ref`,
const localMethod1 = () => {
console.log("Method 1 executed")
}
// ..
}
Export the child component using forwardRef:
const ChildComponent = (props, ref) => {
// ...
}
export default forwardRef(ChildComponent)
Putting it all together
Child Component
import React, { useImperativeHandle, forwardRef } from 'react';
import { View } from 'react-native'
const ChildComponent = (props, ref) => {
useImperativeHandle(ref, () => ({
// methods connected to `ref`
sayHi: () => { sayHi() }
}))
// internal method
const sayHi = () => {
console.log("Hello")
}
return (
<View />
);
}
export default forwardRef(ChildComponent)
Parent Component
import React, { useRef } from 'react';
import { Button, View } from 'react-native';
import ChildComponent from './components/ChildComponent';
const App = () => {
const childRef = useRef()
return (
<View>
<ChildComponent ref={childRef} />
<Button
onPress={() => {
childRef.current.sayHi()
}}
title="Execute Child Method"
/>
</View>
)
}
export default App
There is an interactive demo of this on Expo Snacks:
https://snack.expo.dev/#backupbrain/calling-functions-from-other-components
This explanation is modified from this TutorialsPoint article
You can add a ref to the child component:
<Child ref='child' {...this.props} />
Then call the method on the child like this:
<Button onPress={this.refs.child.myfunc} />
it is in react. i hope it may help you.
class Child extends React.Component {
componentDidMount() {
this.props.onRef(this)
}
componentWillUnmount() {
this.props.onRef(null)
}
method() {
console.log('do stuff')
}
render() {
return <h1>Hello World!</h1>
}
}
class EnhancedChild extends React.Component {
render() {
return <Child {...this.props} />
}
}
class Parent extends React.Component {
onClick = () => {
this.child.method() // do stuff
};
render() {
return (
<div>
<EnhancedChild onRef={ref => (this.child = ref)} />
<button onClick={this.onClick}>Child.method()</button>
</div>
);
}
}
ReactDOM.render(<Parent />, document.getElementById('root'))
Original Solution:
https://jsfiddle.net/frenzzy/z9c46qtv/
https://github.com/kriasoft/react-starter-kit/issues/909
Simple and easy way to Parent --> Child function call
/* Parent.js */
import React, { Component } from "react";
import { TouchableOpacity, Text } from "react-native";
import Child from "./Child";
class Parent extends React.Component {
onChildClick = () => {
this.child.childFunction(); // do stuff
};
render() {
return (
<div>
<Child onRef={(ref) => (this.child = ref)} />
<TouchableOpacity onClick={this.onChildClick}>
<Text>Child</Text>
</TouchableOpacity>
</div>
);
}
}
/* Child.js */
import React, { Component } from "react";
class Child extends React.Component {
componentDidMount() {
this.props.onRef(this);
}
componentWillUnmount() {
this.props.onRef(undefined);
}
childFunction() {
// do stuff
alert("childFunction called");
}
render() {
return <View>Hello World!</View>;
}
}
Original Solution:
https://github.com/kriasoft/react-starter-kit/issues/909
I think you have misunderstood something about component structure.
Assume that your child is a component which generates button for your other components. In this hierarchy your child has to inform it's parent that it was pressed.
child -----> parent
export default class Child extends Component {
return(
<Button onPress={this.props.onPress } />
);
}
In your parent component use child component to generate a button for you. In this way you can use child component any other components as a independent button.
export default class Parent extends Component {
constructor(props) {
super(props);
this.execChildFunct=this.execChildFunct.bind(this)
}
execChildFunct: function() {
console.log('Managed!');
}
return (
<Child onPress = {this.execChildFunct}></Child>
)
}