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
Related
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;
I am fetching all teams from an API in my App.js file and storing all the fetched teams in an array, the state is managed by Redux.
Here is my App.js where i use the getTeams() function to send the teams to Redux.
import React, {useEffect} from 'react';
import { Route, Switch } from 'react-router-dom';
import { useDispatch } from 'react-redux';
import { teamActions } from './store/team-slice';
import Header from './components/layout/Header';
import Footer from './components/layout/Footer';
import Teams from './components/pages/Teams';
import Home from './components/pages/Home';
import TeamInfo from './components/pages/TeamInfo';
const App = ()=> {
const dispatch = useDispatch();
const getTeams = () => {
fetch(`https://www.balldontlie.io/api/v1/teams`)
.then(res => res.json())
.then(data => dispatch(teamActions.sortTeams(data.data)));
}
useEffect(() => {
getTeams();
}, []);
return (
<>
<Header/>
<Switch>
<Route exact path={'/'} component={Home}></Route>
<Route exact path={'/teams'} component={Teams}></Route>
<Route exact path={'/team/:teamName'} component={TeamInfo}></Route>
</Switch>
<Footer/>
</>
);
}
export default App;
In Redux I store the teams in an array called totalTeams:
import { createSlice } from "#reduxjs/toolkit";
const teamInitialState = {totalTeams: [], easternTeams:[], westernTeams:[]};
const teamSlice = createSlice({
name: 'team',
initialState: teamInitialState,
reducers: {
sortTeams(state, action){
state.totalTeams = action.payload;
state.westernTeams = action.payload.filter(team => team.conference === 'West');
state.easternTeams = action.payload.filter(team => team.conference === 'East');
}
}
});
export const teamActions = teamSlice.actions;
export default teamSlice;
I then have a "Team Info" page where I take the team name from the url using useParams
to filter out the current team from the redux array of totalTeams.
I then use that data to fill the page with the team info, everything works until I refresh the page, I then lose all data.
Shouldn't all the data still be there since I am using the Redux array to find the team info? The fetch to the API is made on every page reload and the data is saved to the array so why am I losing state?
import React from 'react';
import { useSelector } from 'react-redux';
import { useParams } from 'react-router-dom';
import Container from '../layout/Container';
import classes from './TeamInfo.module.css'
const TeamInfo = () => {
const teams = useSelector(state => state.team.totalTeams);
const params = useParams();
const teamSlug = params.teamName;
const teamNoDashes = teamSlug.replace(/-/g, ' ');
const currentTeam = teams && teams.find(team => team.full_name.toLowerCase() === teamNoDashes);
return (
<Container>
<div className={classes['team-info-container']}>
<div><img src={`/img/team-logos-grid/${teamSlug}.png`} alt={teamSlug} /></div>
<div>
<h2>{currentTeam.full_name}</h2>
<ul>
<li>City: {currentTeam.city}</li>
<li>Division: {currentTeam.division}</li>
<li>Conference: {currentTeam.conference}</li>
</ul>
</div>
</div>
</Container>
)
}
export default TeamInfo;
First you need check exist of data, and then fields in this object:
loading || (data && (!data.me || !data.getPlaces)) ? ...
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;
...
I built a simple react app that fetch the users from database like mysql
After fetching data I want to pass data to child component and it works sometimes but when refresh page it throw error like data.map is not function
here error message image
my code of parent component
import React,{useEffect, useState ,Context, createContext} from 'react'
import Sidenav from '../components/Sidenav'
import {makeStyles} from '#material-ui/core/styles';
import Content from '../components/ContentParent'
import RightSideNav from '../components/RightSideNav'
import Backdrop from '#material-ui/core/Backdrop';
import Async from 'react-async';
import CircularProgress from '#material-ui/core/CircularProgress';
import { get } from 'js-cookie';
const bodyData = createContext()
const useStyles = makeStyles((theme) => ({
HomeDataContainer_parent:{
backgroundColor:'#F2F2F2',
display:'flex',
},
backdrop: {
zIndex: theme.zIndex.drawer + 1,
color: '#fff',
},
}))
function HomeDataContainer() {
const [data,setData] = useState('')
const [isload,setload] = useState(true)
useEffect(() =>{
async function get(){
fetch('/postsDrawer').then(async data => await data.json()).then(result =>{ setData(result)
setload(false)
})
}
get()
},[data])
const classes = useStyles()
return (
<div className = {classes.HomeDataContainer_parent}>
<Backdrop className={classes.backdrop} open={isload} >
<CircularProgress color="inherit" />
</Backdrop>
{/* sidenav */}
<Sidenav/>
{/* content */}
if(data){
<Content value = {data} />
}
{/* Right side updates */}
<RightSideNav />
</div>
)
}
export default HomeDataContainer
export { bodyData }
child component
import React from 'react'
import {makeStyles} from '#material-ui/core/styles'
import Avatar from '#material-ui/core/Avatar';
import ArrowDropDownTwoToneIcon from '#material-ui/icons/ArrowDropDownTwoTone';
import ArrowDropUpTwoToneIcon from '#material-ui/icons/ArrowDropUpTwoTone';
import LocalOfferTwoToneIcon from '#material-ui/icons/LocalOfferTwoTone';
import Chip from '#material-ui/core/Chip';
import QuestionAnswerIcon from '#material-ui/icons/QuestionAnswer';
import VisibilityIcon from '#material-ui/icons/Visibility';
import Logo from '../images/action.png'
import { BodyData } from '../components/HomeDataContainer'
export default function HomeRecentQuestion(props) {
const classes = useStyles()
const data = props.value
let count = 0
return (
data.map(content => {
let count = content.tags.split(',')
return (
<h1>{content.id}</h1>
)
})
)
}
how to resolve this
1:
Change your data state to this:
const [data,setData] = useState([])
Change useEffect to this:
useEffect(() =>{
if(!data.length)
fetch('/postsDrawer').then(res=> res.json())
.then(result => {
setData(result)
setload(false)
})
},[data])
Change your component to this:
<HomeRecentQuestion value={data} />
You can try using below on the child component.
data && data.map(content => {
let count = content.tags.split(',')
return (
<h1>{content.id}</h1>
)
})
you will change it state of types when you created
like these: const [data, setData] = useState([]);
just you can it than problem will close it.
Starting with GamePage, it provides 2 routes which renders the components GameList and GameDetailPage. Both work fine at first but When i refresh the page for Gamelist component, it still rerenders the page but when i refresh the page for GameDetailPage, i get the error TypeError: Cannot read property 'Location' of undefined. I do not understand why it is unable to fetch data from state whenever i refresh.
gamepage.jsx
import React from "react";
import GamesList from "../../components/games-list/game-list.component";
import { Route } from "react-router-dom";
import GameDetailPage from "../gamedetailpage/gamedetailpage.component";
import {firestore,convertCollectionsSnapshotToMap} from '../../firebase/firebase.utils'
import {connect} from 'react-redux'
import {updateFootballGames} from '../../redux/games/games.actions'
class GamePage extends React.Component {
unsubscribeFromSnapshot=null;
//whenever the component mounts the state will be updated with the football games.
componentDidMount(){
const {updateFootballGames}=this.props
const gameRef=firestore.collection('footballgames')
gameRef.onSnapshot(async snapshot=>{
const collectionsMap=convertCollectionsSnapshotToMap(snapshot)
updateFootballGames(collectionsMap)
})
}
render() {
const { match } = this.props;
return (
<div className="game-page">
<h1>games page</h1>
<Route exact path={`${match.path}`} component={GamesList} />
<Route path={`${match.path}/:linkUrl`} component={GameDetailPage}
/>
</div>
);
}
}
const mapStateToProps=state=>({
games:state.games.games
})
const mapDispatchToProps=dispatch=>({
updateFootballGames:collectionsMap=>
dispatch(updateFootballGames(collectionsMap))
})
export default connect(mapStateToProps, mapDispatchToProps)(GamePage);
gamedetailpage.component.jsx
import React from "react";
import { connect } from "react-redux";
import GamePreview from '../../components/game-preview/game-preview.component'
import GameDetails from '../../components/game-details/game-details.component'
const GameDetailPage = (props) => {
const {games, match} = props
const urlparam =match.params.linkUrl
// const games_array = Object.entries(games)
const gameObj=games[urlparam]
console.log('prop',gameObj)
return (
<div className="game-list">
<GameDetails game = {gameObj}/>
</div>
);
};
const mapStateToProps = (state) => ({
games: state.games.games,
});
export default connect(mapStateToProps)(GameDetailPage);
game_details.component.jsx
import React from 'react';
const GameDetails = (props) => {
console.log(props.game.Location)
return(
<div>
Location:{props.game.Location}
<br/>
Price:{props.game.Price}
</div>
)
}
export default GameDetails;
gamelist.component.jsx
import React from "react";
import './game-list.styles.scss'
import GamePreview from "../game-preview/game-preview.component";
import {connect} from 'react-redux'
const GameList=(props)=>{
const {games}=props
console.log(games)
const game_list=Object.entries(games)
console.log(game_list)
return (
<div className="game-list">
{game_list.map(game =>
<GamePreview game = {game[1]}/>)}
</div>
);
}
const mapStateToProps=state=>({
games:state.games.games
})
export default connect(mapStateToProps)(GameList);
gamepreview.component.jsx
import React from "react";
import "./game-preview.styles.scss";
import { withRouter, Route } from "react-router-dom";
import GamePreviewDetail from "../game-preview-detail/game-preview-detail.component";
const GamePreview = (props) => {
const { Location, Time, linkUrl, Price } = props.game;
const { history, match } = props;
return (
<div
className="game-preview"
onClick={() => history.push(`${match.url}/${linkUrl}`)}
>
<div className="game-preview-image">
<p>Picture goes here</p>
</div>
{/* <GamePreviewDetail name = {Location} price={Price}/> */}
<p>Location:{Location}</p>
<p>Price:{Price}</p>
</div>
);
};
export default withRouter(GamePreview);
app.js
import React from 'react';
import './App.css';
//import dependencies
import { Route, Switch } from "react-router-dom";
//import pages
import HomePage from './pages/homepage/homepage'
import GamesPage from './pages/gamespage/gamespage'
import SignInSignUp from './pages/signin-signup-page/signin-signup-page'
import GameDetailPage from "./pages/gamedetailpage/gamedetailpage.component";
import Header from './components/header/header.component';
import { auth, createUserProfileDocument } from './firebase/firebase.utils';
class App extends React.Component{
constructor() {
super();
this.state = {
currentUser: null
}
}
unsubscribeFromAuth = null
componentDidMount() {
this.unsubscribeFromAuth = auth.onAuthStateChanged(async userAuth => {
if (userAuth) {
const userRef = await createUserProfileDocument(userAuth);
// check if the snapshot has changed (subscribe)
// get the user that we just created or that already exists in the db
userRef.onSnapshot(snapshot => {
this.setState({
currentUser: {
id: snapshot.id,
...snapshot.data()}
})
})
} else {
this.setState({currentUser: userAuth})
}
})
}
componentWillUnmount() {
this.unsubscribeFromAuth();
}
render(){
return(
<div>
<Header currentUser = {this.state.currentUser}/>
<Switch>
<Route exact path="/" component={HomePage} />
<Route path="/games" component={GamesPage} />
<Route exact path="/signin" component={SignInSignUp} />
</Switch>
</div>
)
}
}
export default App;
I would try using useParams hook instead. Then capturing any changes of linkUrl with useEffect hook. Also introducing gameObj with useState.
useParams returns an object of key/value pairs of URL parameters. Use it to access match.params of the current <Route>.
If you're familiar with React class lifecycle methods, you can think of useEffect Hook as componentDidMount, componentDidUpdate, and componentWillUnmount combined.
Try to modify <GameDetailPage /> component as the following:
import React, { useState, useEffect } from 'react';
import { useParams } from "react-router-dom";
// other imports
const GameDetailPage = (props) => {
const { games } = props;
let { linkUrl } = useParams();
const [ gameObj, setGameObj ] = useState(null);
useEffect(() => {
if (games) {
const newGameObj = games[linkUrl];
console.log('game object', newGameObj);
setGameObj(newGameObj);
}
}, [games, linkUrl]);
return <div className="game-list">
{ gameObj && <GameDetails game={ gameObj } /> }
</div>
}
+1 - null check:
Also you can see a null check in the return statement for gameObj which helps rendering only that case once you have a value in games array with found linkUrl value.
I hope this helps!