how to dynamically import a react component and call it? - javascript

I have a react component that contains an SVG image.
and i import it like this
import MessageIcon from '../static/styles/svg/messageIcon';
However as part of code splitting, i want to dynamically import it instead of static import.
I tried like this but does not work
import('../static/styles/svg/messageIcon').then(MessageIcon => { return <MessageIcon /> });
can anyone tell me how i can dynamically import and call this component? thanks in advance

You can import dynamical like this :
import React, { useEffect, useState } from "react";
const App = () => {
const [state, setState] = useState<any>();
useEffect(() => {
(async () => {
const i = await import("../../assets/img/user.svg");
setState(i);
})();
}, []);
return <img alt={"test dynamic"} src={state?.default} />;
};
export default App;
Also, you can write it with ref too:
import React, { useEffect, useRef } from "react";
const App = () => {
const ref = useRef<any>();
useEffect(() => {
(async () => {
const i = await import("../../assets/img/user.svg");
ref.current.src = i.default;
})();
}, []);
return <img alt={"test dynamic"} ref={ref} />;
};
export default App;
import dynamical React component
import React from "react";
const App = () => {
const nameIcon = "iconMessage";
const IconMessage = React.lazy(() => import(/* webpackChunkName: "icon" */ `./${nameIcon}`));
return <IconMessage />;
};
export default App;

Related

react-rte giving TypeError: r.getEditorState is not a function in next js

I have a nextjs project and it has a react-rte component
the react-rte component is displayed correctly but when I go to some other component and click back in the browsers back button I get the following error:
Unhandled Runtime Error TypeError: r.getEditorState is not a function
When I comment out the react-rte componnet the error no longer occurs
react-rte component
import React, { useState, useEffect } from "react";
import dynamic from "next/dynamic";
import PropTypes from "prop-types";
//import the component
const RichTextEditor = dynamic(() => import("react-rte"), { ssr: false });
const MyStatefulEditor = ({ onChange }) => {
const [value, setValue] = useState([]);
console.log(value.toString("html"));
useEffect(() => {
const importModule = async () => {
//import module on the client-side to get `createEmptyValue` instead of a component
const module = await import("react-rte");
console.log(module);
setValue(module.createEmptyValue());
};
importModule();
}, []);
const handleOnChange = (value) => {
setValue(value);
if (onChange) {
onChange(value.toString("html"));
}
};
return <RichTextEditor value={value} onChange={handleOnChange} />;
};
MyStatefulEditor.propTypes = {
onChange: PropTypes.func,
};
export default MyStatefulEditor;
You can add a condition to check value before rendering RichTextEditor
import React, { useState, useEffect } from "react";
import dynamic from "next/dynamic";
import PropTypes from "prop-types";
import { useRouter } from "next/router";
//import the component
const RichTextEditor = dynamic(() => import("react-rte"), { ssr: false });
const MyStatefulEditor = ({ onChange }) => {
const [value, setValue] = useState();
const router = useRouter();
useEffect(() => {
const importModule = async () => {
//import module on the client-side to get `createEmptyValue` instead of a component
const module = await import("react-rte");
setValue(module.createEmptyValue());
};
importModule();
}, [router.pathname]);
const handleOnChange = (value) => {
setValue(value);
if (onChange) {
onChange(value.toString("html"));
}
};
//if `value` from react-rte is not generated yet, you should not render `RichTextEditor`
if (!value) {
return null;
}
return <RichTextEditor value={value} onChange={handleOnChange} />;
};
MyStatefulEditor.propTypes = {
onChange: PropTypes.func
};
export default MyStatefulEditor;
You can verify the implementation with the live example and the sandbox
Try this
import React, { useState, useEffect } from "react";
import dynamic from "next/dynamic";
import PropTypes from "prop-types";
const RichTextEditor = dynamic(() => import("react-rte"), { ssr: false });
const MyStatefulEditor = ({ onChange }) => {
const [value, setValue] = useState([]);
useEffect(() => {
// set the state value using the package available method
setValue(RichTextEditor.createEmptyValue())
}, []);
const handleOnChange = (value) => {
setValue(value);
if (onChange) {
onChange(value.toString("html"));
}
};
return <RichTextEditor value={value} onChange={handleOnChange} />;
};
MyStatefulEditor.propTypes = {
onChange: PropTypes.func,
};
export default MyStatefulEditor;
Like I mention in my previous comment, I notice you are importing the react-rte package twice.
In the useEffect hook you do an import to initialise the value state, by looking at the example code found here
You can achieve that using RichTextEditor.createEmptyValue() which comes from the already imported package.
You will noticed that I change the import, is not dynamic, try that and if is it works if so then try doing the dynamic import if that is what you need.

React native custom fonts not loading in, not sure why?

I did everything like in the documentation, but my Custom Fonts do not want to load in. I could wait for hours and nothing happens...
This is my App.js:
import React, { useState } from 'react';
import AuthNavigation from './AuthNavigation';
import useFonts from './shared/useFonts';
import LoadingScreen from './screens/LoadingScreen';
export default function App() {
const [isReady,setIsReady] = useState(false);
const LoadFonts = async () => {
await useFonts();
setIsReady(true);
}
useEffect(() => {
LoadFonts();
},[])
if (!isReady) {
return(
<LoadingScreen/>
)
}
else{
return (
<AuthNavigation/>
);
}
}
And this is my useFont.js
import Font from 'expo-font';
export default useFont = async () =>
await Font.loadAsync({
QuicksandMedium: require('../assets/Fonts/Quicksand-Medium.ttf'),
AmaticSCRegular: require('../assets/Fonts/AmaticSC-Regular.ttf')
})
There is no error printed in the console, so I have no clue what I am doing wrong :/
I'm not sure, but maybe that if should be the problem. Try with this:
import React, { useState } from 'react';
import AuthNavigation from './AuthNavigation';
import useFonts from './shared/useFonts';
import LoadingScreen from './screens/LoadingScreen';
export default function App() {
const [isReady,setIsReady] = useState(false);
const LoadFonts = async () => {
await useFonts();
setIsReady(true);
};
useEffect(() => {
LoadFonts();
},[]);
return (
<>
{isReady ?
<AuthNavigation/>
:
<LoadingScreen/>
}
</>
);
}
Solution is to import your Fonts like this:
import * as Font from 'expo-font';

How to transport a function and an object to a functional component in react

I'm still have problems with typescript and react so please be condescending. Thank you in advance guys.
I'm trying to transport a function and an object to a component
So here is my component named WordItem
import React, {FC, useContext} from "react"
import { IWord } from "../../../models/IWord"
import {Context} from "../../../index";
const WordItem: FC<IWord> = (word) => {
const {store} = useContext(Context)
async function deleteWord () {
await store.deleteWord(word.id)
}
return (
<div key={word.id}>
<div>
<div>{word.word}</div>
<div>{word.wordT}</div>
</div>
<div>
<div>{word.instance}</div>
<div>{word.instanceT}</div>
<button onClick={() => {deleteWord()}}>delete</button>
</div>
</div>
)
}
export default WordItem
And this is my App.tsx file:
import React, {FC, useContext, useState} from 'react';
import {Context} from "./index";
import {observer} from "mobx-react-lite";
import {IUser} from "./models/IUser";
import WordService from "./services/WordService"
import { IWord } from './models/IWord';
import WordItem from './components/UI/word/WordItem';
const App: FC = () => {
const {store} = useContext(Context)
const [users, setUsers] = useState<IUser[]>([])
const [words, setWords] = useState<IWord[]>([])
async function getWords() {
try {
const response = await WordService.fetchWords()
setWords(response.data)
} catch (e) {
console.log(e)
}
}
return (
<div>
<button onClick={getWords}>Get words</button>
{words.reverse().map(word => <WordItem {...word}/>)}
{words.length == 0? 'there is no words': ''}
</div>
)
}
export default observer(App);
As you can see i successfully transported object word in a component but i really dont know the way to transport a function also. <WordItem {getWords, ...word}/> or <WordItem getWords={getWords} { ...word}/> does not help. Like how to properly put then into <WordItem/>
If you want to pass down both the word variable and the getWords function to the WordItem component, you should do it as follow:
<WordItem word={word} getWords={getWords} />
Then you need to adapt your component prop types to match these props:
const WordItem: FC<{word: IWord, getWords: () => Promise<void>}> = ({word, getWords}) => {
...
I am using object destructuring in the code above to get the two props from the prop parameter which is equivalent to doing something like:
const WordItem: FC<{word: IWord, getWords: () => Promise<void>}> = (props) => {
const word = props.word;
const getWords = props.getWords;
...

Sibling component not re-rerendering on state change (using useEffect, useState and Context)

In my Main.js I create a first global state with a username and a list of users I'm following.
Then, both the Wall component and FollowingSidebar render the list of follows and their messages (plus the messages of the main user).
So far so good. But in a nested component inside FollowingSidebar called FollowingUser I have an onClick to remove a user. My understanding is that, because I change the state, useEffect would take care of the Wall component to re-render it, but nothing happens... I've checked several examples online but nothing has helped my use case so far.
Needless to say I'm not overly experienced with React and Hooks are a bit complex.
The code here:
Main.js:
import React, { useEffect, useState } from "react";
import ReactDom from "react-dom";
import db from "./firebase.js";
// Components
import Header from "./components/Header";
import FollowingSidebar from "./components/FollowingSidebar";
import SearchUsers from "./components/SearchUsers";
import NewMessageTextarea from "./components/NewMessageTextarea";
import Wall from "./components/Wall";
// Context
import StateContext from "./StateContext";
function Main() {
const [mainUser] = useState("uid_MainUser");
const [follows, setFollows] = useState([]);
const setInitialFollows = async () => {
let tempFollows = [mainUser];
const user = await db.collection("users").doc(mainUser).get();
user.data().following.forEach(follow => {
tempFollows.push(follow);
});
setFollows(tempFollows);
};
useEffect(() => {
setInitialFollows();
}, []);
const globalValues = {
mainUserId: mainUser,
followingUsers: follows
};
return (
<StateContext.Provider value={globalValues}>
<Header />
<FollowingSidebar />
<SearchUsers />
<NewMessageTextarea />
<Wall />
</StateContext.Provider>
);
}
ReactDom.render(<Main />, document.getElementById("app"));
if (module.hot) {
module.hot.accept();
}
FollowingSidebar component:
import React, { useState, useEffect, useContext } from "react";
import db from "../firebase.js";
import StateContext from "../StateContext";
import FollowingUser from "./FollowingUser";
export default function FollowingSidebar() {
const { followingUsers } = useContext(StateContext);
const [users, setUsers] = useState(followingUsers);
useEffect(() => {
const readyToRender = Object.values(followingUsers).length > 0;
if (readyToRender) {
db.collection("users")
.where("uid", "in", followingUsers)
.get()
.then(users => {
setUsers(users.docs.map(user => user.data()));
});
}
}, [followingUsers]);
return (
<section id="following">
<div className="window">
<h1 className="window__title">People you follow</h1>
<div className="window__content">
{users.map((user, index) => (
<FollowingUser avatar={user.avatar} username={user.username} uid={user.uid} key={index} />
))}
</div>
</div>
</section>
);
}
FollowingUser component:
import React, { useState, useContext } from "react";
import db from "../firebase.js";
import firebase from "firebase";
import StateContext from "../StateContext";
export default function FollowingUser({ avatar, username, uid }) {
const { mainUserId, followingUsers } = useContext(StateContext);
const [follows, setFollows] = useState(followingUsers);
const removeFollow = e => {
const userElement = e.parentElement;
const userToUnfollow = userElement.getAttribute("data-uid");
db.collection("users")
.doc(mainUserId)
.update({
following: firebase.firestore.FieldValue.arrayRemove(userToUnfollow)
})
.then(() => {
const newFollows = follows.filter(follow => follow !== userToUnfollow);
setFollows(newFollows);
});
userElement.remove();
};
return (
<article data-uid={uid} className="following-user">
<figure className="following-user__avatar">
<img src={avatar} alt="Profile picture" />
</figure>
<h2 className="following-user__username">{username}</h2>
<button>View messages</button>
{uid == mainUserId ? "" : <button onClick={e => removeFollow(e.target)}>Unfollow</button>}
</article>
);
}
Wall component:
import React, { useState, useEffect, useContext } from "react";
import db from "../firebase.js";
import Post from "./Post";
import StateContext from "../StateContext";
export default function Wall() {
const { followingUsers } = useContext(StateContext);
const [posts, setPosts] = useState([]);
useEffect(() => {
console.log(followingUsers);
const readyToRender = Object.values(followingUsers).length > 0;
if (readyToRender) {
db.collection("posts")
.where("user_id", "in", followingUsers)
.orderBy("timestamp", "desc")
.get()
.then(posts => setPosts(posts.docs.map(post => post.data())));
}
}, [followingUsers]);
return (
<section id="wall">
<div className="window">
<h1 className="window__title">Latest messages</h1>
<div className="window__content">
{posts.map((post, index) => (
<Post avatar={post.user_avatar} username={post.username} uid={post.user_id} body={post.body} timestamp={post.timestamp.toDate().toDateString()} key={index} />
))}
</div>
</div>
</section>
);
}
StateContext.js:
import { createContext } from "react";
const StateContext = createContext();
export default StateContext;
The main issue is the setting of state variables in the Main.js file (This data should actually be part of the Context to handle state globally).
Below code would not update our state globally.
const globalValues = {
mainUserId: mainUser,
followingUsers: follows
};
We have to write state in a way that it get's modified on the Global Context level.
So within your Main.js set state like below:
const [globalValues, setGlobalValues] = useState({
mainUserId: "uid_MainUser",
followingUsers: []
});
Also add all your event handlers in the Context Level in Main.js only to avoid prop-drilling and for better working.
CODESAND BOX DEMO: https://codesandbox.io/s/context-api-and-rendereing-issue-uducc
Code Snippet Demo:
import React, { useEffect, useState } from "react";
import FollowingSidebar from "./FollowingSidebar";
import StateContext from "./StateContext";
const url = "https://jsonplaceholder.typicode.com/users";
function App() {
const [globalValues, setGlobalValues] = useState({
mainUserId: "uid_MainUser",
followingUsers: []
});
const getUsers = async (url) => {
const response = await fetch(url);
const data = await response.json();
setGlobalValues({
...globalValues,
followingUsers: data
});
};
// Acts similar to componentDidMount now :) Called only initially
useEffect(() => {
getUsers();
}, []);
const handleClick = (id) => {
console.log(id);
const updatedFollowingUsers = globalValues.followingUsers.filter(
(user) => user.id !== id
);
setGlobalValues({
...globalValues,
followingUsers: updatedFollowingUsers
});
};
return (
<StateContext.Provider value={{ globalValues, handleClick }}>
<FollowingSidebar />
</StateContext.Provider>
);
}
export default App;

Avoid recursive rendering with promises?

Heres my simplified React component:
import React from "react"
import {useSelector, useDispatch} from "react-redux"
import { useRouteMatch } from "react-router-dom"
import gameService from "../services/game"
import {setGame} from "../reducers/game" //action creator
const Game = () => {
const game = useSelector(state => state.game)
const dispatch = useDispatch()
const match = useRouteMatch('/games/:id')
gameService.oneGame(match.params.id).then(g => dispatch(setGame(g)))
const gameInfo = (game) => {
...some proceessing
return(JSX containig info about the game)
}
return(
game ? <div>
<h2>{game.name}</h2>
{gameInfo(game)}
</div> :
loading
)
}
export default Game
Component is called from App.js:
<Route path="/games/:id">
<Game />
</Route>
Everything works but the site renders infinitely. Does the promise resolve after the component has rendered and this renders the component again, or what is happenig here? And what is the easiest fix?
I think you may want to put the call to gameService in a useEffect hook so that it is only called when match.params.id and dispatch change rather than every time the component is re-rendered.
Try amending it to be:
import React, { useEffect } from "react"
import {useSelector, useDispatch} from "react-redux"
import { useRouteMatch } from "react-router-dom"
import gameService from "../services/game"
import {setGame} from "../reducers/game" //action creator
const Game = () => {
const game = useSelector(state => state.game)
const dispatch = useDispatch()
const match = useRouteMatch('/games/:id')
useEffect(() => {
gameService.oneGame(match.params.id).then(g => dispatch(setGame(g)))
}, [match.params.id, dispatch]);
const gameInfo = (game) => {
...some proceessing
return(JSX containig info about the game)
}
return(
game ? <div>
<h2>{game.name}</h2>
{gameInfo(game)}
</div> :
loading
)
}
export default Game

Categories