The following simple React component is importing a JSON file (data.js) as an object and list the items inside it.
List.js
import React from 'react'
import jsonResponse from './data'
function ZooList ({ setID }) {
const setURL = (e) => {
window.history.pushState(null, null, '/' + e)
setID(e)
}
const list = jsonResponse.animals.map((item) => {
return (
<li key={item.id}>
<div>
<img
src={item.image_url}
alt={item.name}
onClick={() => setID(item.id)}
/>
<h3>{item.name}</h3>
<p>
<b>Distribution</b>: {item.distribution}
</p>
<button onClick={() => setURL(item.id)}>More...</button>
</div>
</li>
)
})
return (
<ul>
{list}
</ul>
)
}
export default List
Now in the above page, if you click on button "More...", it calls another React component called Tile.js as fallow:
Tile.js
import React from 'react'
import jsonResponse from './data'
function Tile ({ setID, newID }) {
const clearURL = (e) => {
window.history.pushState(null, null, '/')
setID(null)
}
return (
<div>
<div>
<img
src={jsonResponse.animals[newID].image_url}
alt={jsonResponse.animals[newID].name}
/>
<h2>{jsonResponse.animals[newID].name}</h2>
<p><b>Distribution</b>: {jsonResponse.animals[newID].distribution}</p>
<StyledParagraph>{jsonResponse.animals[newID].details.long}</StyledParagraph>
<button onClick={() => clearURL(null)}>Back to overview</button>
</div>
</div>
)
}
export default Tile
The problem is that the second component is also importing the JSON file (data.js).
How can I avoid importing the data.js twice?
Generally, what would be a better way to write this app?
Imports are cached, so if you return directly a JSON with import jsonResponse from './data', the first component will import it, while the second will get it from import cache;
You can try for example, to export an instance of a class, change one of its property and then check that property in another component that make use of that import.
A ready-to-pick and very common usage example of that cache is the configureStore of react-boilerplate: it exports the store instance so whatever component import it will refer to the same store.
Related
I stored my social links data on firebase, and now I want to make a <li> list of my social links but the icon object I have here, is not showing me the icons instead it's showing me the elements like: <BsBehance />. It should be displayed as icon, how can I do that?
Firebase data:-
Code:-
import { useEffect, useState } from "react";
import * as ReactIcons from "react-icons/fa";
import MainWrapper from "./MainWrapper";
import classes from "./pages.module.css";
export default function Footer() {
const [socialLinks, setSocialLinks] = useState([]);
useEffect(() => {
const fetchSocilLinks = async () => {
const res = await fetch(
"https://mohitdevelops-d64e5-default-rtdb.asia-southeast1.firebasedatabase.app/accounts.json"
);
const data = await res.json();
let loadedData = [];
for (const keys in data) {
loadedData.push({
url: data[keys].url,
icon: data[keys].icon,
id: data[keys].id,
});
}
setSocialLinks(loadedData);
};
fetchSocilLinks().catch((err) => {
console.log(err.message);
});
}, [socialLinks]);
console.log(socialLinks);
return (
<footer className={classes.footer__wrap}>
<MainWrapper>
<p>Connect with me:</p>
<ul className={classes.social_links}>
{socialLinks?.map(({ icon, id, url }) => {
const iconLink = icon.split(/\<|\/>/).filter((e) => e)[0];
const IconsComponent = ReactIcons[iconLink];
return (
<li key={id}>
<a href={url} target="_blank">
<IconsComponent />
</a>
</li>
);
})}
</ul>
</MainWrapper>
</footer>
);
}
And its showing me like this:-
Everything data is coming fine, just need to know how to display an element which is working like an icon.
Since el.icon is a string, it's being displayed as a string. If you want to render the component based on that dynamic string, you need to have an object, where the key name is the string you will get from the server. And the value, can be the component itself.
When you insert a string inside {}, React will render it as a string, even if they're in the < /> format.
And also, try to never use dangerouslySetInnerHTML, since it has high security issues.
Here is a codesanbox, for your above case. Please check this:
You need to use dangerouslySetInnerHTML like this
<ul className={classes.social_links}>
{socialLinks.map((el) => {
return (
<li key={el.id}>
<a href={el.url} target="_blank">
<span dangerouslySetInnerHTML={{__html: el.icon}} />
</a>
</li>
);
})}
</ul>
The icon which need to display should be import from the react-icon/bs and that icon name is coming from the firebase data, so it can be import using dynamic import in react.js but for dynamic import you need to handle loading separately, but I have provided easy way.
Need to install react-icon
npm install react-icons --save
And then import all the Icon
import * as ReactIcons from "react-icons/bs";
And add this line of code
<ul>
{socialLinks?.map(({ icon, id, url }) => {
const iconLink = icon.split(/\<|\/>/).filter((e) => e)[0];
const Compoenent = ReactIcons[iconLink];
return (
<li key={id}>
<a href={url}>
<Compoenent />
</a>
</li>
);
})}
</ul>
I'm doing a simple todo list using React. What I fail to do is to remove an item once I click on the button.
However, if I click delete and then add a new item, it's working, but only if I add a new todo.
Edit:I've edited the post and added the parent componenet of AddMission.
import React,{useState}from 'react';
import { Button } from '../UI/Button/Button';
import Card from '../UI/Card/Card';
import classes from '../toDo/AddMission.module.css'
const AddMission = (props) => {
const [done,setDone]=useState(true);
const doneHandler=(m)=>{
m.isDeleted=true;
}
return (
<Card className={classes.users}>
<ul>
{props.missions.map((mission) => (
<li className={mission.isDeleted?classes.done:''} key={mission.id}>
{mission.mission1}
<div className={classes.btn2}>
<Button onClick={()=>{
doneHandler(mission)
}} className={classes.btn}>Done</Button>
</div>
</li>
)) }
</ul>
</Card>
);
};
export default AddMission;
import './App.css';
import React,{useState} from 'react';
import { Mission } from './components/toDo/Mission';
import AddMission from './components/toDo/AddMission';
function App() {
const [mission,setMission]=useState([]);
const [isEmpty,setIsEmpty]=useState(true);
const addMissionHandler = (miss) =>{
setIsEmpty(false);
setMission((prevMission)=>{
return[
...prevMission,
{mission1:miss,isDeleted:false,id:Math.random().toString()},
];
});
};
return (
<div className="">
<div className="App">
<Mission onAddMission={addMissionHandler}/>
{isEmpty?<h1 className="header-title">Start Your Day!</h1>:(<AddMission isVisible={mission.isDeleted} missions={mission}/>)}
</div>
</div>
);
}
const doneHandler=(m)=>{
m.isDeleted=true;
}
This is what is causing your issue, you are mutating an object directly instead of moving this edit up into the parent. In react we don't directly mutate objects because it causes side-effects such as the issue you are having, a component should only re-render when its props change and in your case you aren't changing missions, you are only changing a single object you passed in to your handler.
Because you haven't included the code which is passing in the missions props, I can't give you a very specific solution, but you need to pass something like an onChange prop into <AddMission /> so that you can pass your edited mission back.
You will also need to change your function to something like this...
const doneHandler = (m) =>{
props.onChange({
...m,
isDeleted: true,
});
}
And in your parent component you'll then need to edit the missions variable so when it is passed back in a proper re-render is called with the changed data.
Like others have mentioned it is because you are not changing any state, react will only re-render once state has been modified.
Perhaps you could do something like the below and create an array that logs all of the ids of the done missions?
I'm suggesting that way as it looks like you are styling the list items to look done, rather than filtering them out before mapping.
import React, { useState } from "react";
import { Button } from "../UI/Button/Button";
import Card from "../UI/Card/Card";
import classes from "../toDo/AddMission.module.css";
const AddMission = (props) => {
const [doneMissions, setDoneMissions] = useState([]);
return (
<Card className={classes.users}>
<ul>
{props.missions.map((mission) => (
<li
className={
doneMissions.includes(mission.id)
? classes.done
: ""
}
key={mission.id}
>
{mission.mission1}
<div className={classes.btn2}>
<Button
onClick={() => {
setDoneMissions((prevState) => {
return [...prevState, mission.id];
});
}}
className={classes.btn}
>
Done
</Button>
</div>
</li>
))}
</ul>
</Card>
);
};
export default AddMission;
Hope that helps a bit!
m.isDeleted = true;
m is mutated, so React has no way of knowing that the state has changed.
Pass a function as a prop from the parent component that allows you to update the missions state.
<Button
onClick={() => {
props.deleteMission(mission.id);
}}
className={classes.btn}
>
Done
</Button>;
In the parent component:
const deleteMission = (missionId) => {
setMissions(prevMissions => prevMissions.map(mission => mission.id === missionId ? {...mission, isDeleted: true} : mission))
}
<AddMission missions={mission} deleteMission={deleteMission} />
I developed a web application and it is a dynamic site. Can I place a multilingual option for my site? I go further to develop a multilingual application? I have got through many provided solution with i18next but thats the solution for static data.
I have reusable components with props. When I map through the data imported from JSON, I can't handle and apply i18next (static key concept). Is there any solution please.
For more details, here is the scenario,
import React, {useEffect, useState} from 'react';
import ObjectiveCard from './ObjectiveCard';
// import objectiveData from '../../../../assets/resume_eng.json';
// import objectiveData from '../../../../assets/resume_dk.json';
import axios from 'axios';
const Objective = () => {
const [objective, setObjective] = useState(null)
useEffect(() => {
axios.get('http://localhost:9000/objective')
.then(response => setObjective(response.data))
}, [])
return (
<div>
{objective && objective.map((o, index) => {
return(
<ObjectiveCard
key={index}
myName={o.name}
jobTitle={o.jobTitle}
objective={o.objective}
/>
)
})}
</div>
)
}
export default Objective;
and child component is,
const ObjectiveCard = (props) => {
return (
<div>
<h1> {props.myName} </h1>
<h3> {props.jobTitle} </h3>
<p>
{props.objective}
</p>
</div>
);
}
export default ObjectiveCard;
I want to pass props from one component to another, and use it in the second one for an import above the component declaration
This is for using the same component, with no need to create it 4 times, every time with another SVG.
I'm using React, Javascript, Webpack, babel.
I'm also using svgr/webpack to create a component from an SVG picture, and it's crucial for me to use SVG not < img >.
import React from 'react';
import RightNavItem from './right_nav_item';
const RightNav = ({navitems}) => {
const rightNavItems = navitems.map( (item) => {
return <RightNavItem name={ item }/>
});
return(
<div className="rightnav">
{rightNavItems}
</div>
);
};
.
export default RightNav;
import React from 'react';
const RightNavItem = ({ name }) => {
const svgpath = `../../../../resources/img/navbar/${name}.svg`;
return(
<div>
<img src={ svgpath } style={{height: '25px'}}/>
<span>{ name }</span>
</div>
);
};
export default RightNavItem;
And I want to achieve being able to do this:
import React from 'react';
import SvgPicture from '../../../../resources/img/navbar/{name}.svg';
const RightNavItem = ({ name }) => {
return(
<div>
<SvgPicture />
<span>{ name }</span>
</div>
);
};
export default RightNavItem;
.
Ok so I went back and implemented the whole thing on my local app to get exactly what you need. I am editing my original answer. Hope this solves your issue.
The parent:
import React from 'react';
import { ReactComponent as svg } from 'assets/img/free_sample.svg';
import RightNavItem from './RightNavItem';
const LOGOS = [
{ name: 'home', svg },
{ name: 'home', svg },
];
const RightNav = () => (
<div>
{LOGOS.map(logo => (
<RightNavItem name={logo.name}>
<logo.svg />
</RightNavItem>
))}
</div>
);
export default RightNav;
The child:
import React from 'react';
const RightNavItem = ({ name, children }) => (
<div>
{children}
<span>{name}</span>
</div>
);
export default RightNavItem;
You don't need to import the svg as I did, if you are able to use svg as a component in your webpack config then continue to do what you were doing before.
I managed to do it in a kind of ugly way, but it works.
The problem is if I have more than 4 items, then using it without the map() function can be really annoying.
I used {props.children}, and instead of using map(), I added the 4 times, each with different 'SVG' component child and different props 'name', that way the component only gets initialized at the RightNavItem level.
IF SOMEONE KNOWS how can I use this with the map() function, It'll help a lot!
Thanks to everyone who helped!
For example:
const RightNav = (props) => {
return(
<div className = "rightnav">
<RightNavItem name = {home}>
<HomeSVG />
</RightNavItem>
<RightNavItem name = {profile}>
<ProfileSVG />
</RightNavItem>
.
.
.
</div>
);
};
And in the RightNavItem:
const RightNavItem = (props) => {
return(
<div>
{props.children}
<span>{ props.name }</span>
</div>
);
};
I've been struggling with this for a couple days, and any help would be appreciated.
In this component, I have tried to do an HTTP call to my server and database. After parsing the response, using JSON.parse, I am getting back a correctly formed JSON object. I then want to map through that object and for each return a new component (called HistoryItem).
The code below attempts to do this by placing the object into the component state, but it is causing an infinite refresh loop. Previously I had tried a functional component.
The original iteration of this component did work. But it pulled a static JSON object from my client side files. Therefore, I am confident code works without the http call.
It seems to me I am doing something wrong with the async, which is disallowing the JSON object received asynchronously from being rendered.
Below is the main component. Note the component imports the username from redux. This feeds the HTTP call, so that it retrieves only records associated with the logged in user. Again, everything looks fine on the server/database end...
import React, {Component} from 'react';
import style from './history.css';
import HistoryItem from './HistoryItem/historyItem';
import data from '../../config/fakermyhistory.json';
import {Link} from 'react-router-dom';
import {connect} from 'react-redux';
import axios from 'axios';
class History extends Component {
constructor(props) {
super(props);
this.state = {
compiledList:[]
}
}
getData(){
this.state.compiledList.map((call, i) => {
const shaded = (call.rated) ? 'lightgrey' : 'white';
console.log("shaded", shaded);
return(
<Link to={`/reviewpage/${call._id}`} key={call._id}
style={{ textDecoration: 'none', color:'lightgrey'}}>
<div style={{backgroundColor:shaded}}>
<hr/>
<HistoryItem call={call}/>
</div>
</Link>
)
})
}
render(){
axios.post('/api/history', {username: this.props.username})
.then((res) => {
const array = JSON.parse(res.request.response);
this.setState({compiledList: array})
console.log("res", array);}
).catch((err) => console.log("err", err));
return (
<div className={style.container}>
<div className={style.historyHeader}>
<div className={style.historyHeaderText}>
Your Call History
</div>
</div>
<div className={style.historyList}>
{this.getData()};
</div>
</div>
)
}
}
const mapStateToProps = state => {
return {
username:state.auth.username
};
}
export default connect(mapStateToProps, null)(History);
Thanks in advance if you can help.
Here is another version using it as a functional component. Also doesn't render (although no errors on this one)
import React, {Component} from 'react';
import style from './history.css';
import HistoryItem from './HistoryItem/historyItem';
import data from '../../config/fakermyhistory.json';
import {Link} from 'react-router-dom';
import {connect} from 'react-redux';
import axios from 'axios';
const History =(props)=> {
const getData=(props)=>{
console.log("props", props);
axios.post('/api/history', {username: props.username})
.then((res) => {
const array = JSON.parse(res.request.response);
console.log("array", array);
array.map((call, i) => {
const shaded = (call.rated) ? 'lightgrey' : 'white';
console.log("shaded", shaded);
return(
<Link to={`/reviewpage/${call._id}`} key={call._id}
style={{ textDecoration: 'none', color:'lightgrey'}}>
<div style={{backgroundColor:shaded}}>
<hr/>
<HistoryItem call={call}/>
</div>
</Link>
)
})
}
).catch((err) => console.log("err", err));
}
return (
<div className={style.container}>
<div className={style.historyHeader}>
<div className={style.historyHeaderText}>
Your Call History
</div>
</div>
<div className={style.historyList}>
{getData(props)};
</div>
</div>
)
}
const mapStateToProps = state => {
return {
username:state.auth.username
};
}
export default connect(mapStateToProps, null)(History);
Instead of calling axios in render function, try to invoke it from componentDidMount.
This will help you prevent the infinite loop.
To return the components rendered within the map function, it was necessary to add a "return" command before the map function was called:
return array.map((call, i) => {...