Render the component lazily, on the select of a value change - React - javascript

How to render the component lazily, on the select of a value change.
Here is my below snippet, i am not able to see the title, nothing is rendering
App.js
import React, { useState } from "react";
import "./styles.css";
import { SectionOne, SectionTwo } from "./ChildComponents";
export default function App() {
const [state, setState] = useState(null);
const sectionToBeDisplayed = {
section_one: (title = "section one") => <SectionOne title={title} />,
section_two: (title = "section two") => <SectionTwo title={title} />
};
const handleSelectChange = (e) => {
setState(e.target.value);
};
return (
<div className="App">
<select onChange={handleSelectChange}>
<option disabled selected value>
{" "}
-- select an option --{" "}
</option>
<option value="section_one">SectionOne</option>
<option value="section_two">SectionTwo</option>
</select>
{sectionToBeDisplayed[state]}
</div>
);
}
ChildComponents.js
import React from "react";
export const SectionOne = ({ title }) => {
return <h1>{title}</h1>;
};
export const SectionTwo = ({ title }) => {
return <h2>{title}</h2>;
};
So based on the selection only i need to load this component, I am newbie to react, read that we can use React.lazy but i don't see any use case like this. Also whenever i change it should not build the dom again. Should we use useMemo, I am not clear to use React.memo or useMemo, Which one is better.

You need to invoke the function component:
// Component is a function component
const Component = (title = "section one") => <SectionOne title={title} />;
// Invoke it
<Component/>
const sectionToBeDisplayed = {
section_one: ...
};
export default function App() {
...
const SectionComponent = sectionToBeDisplayed[state];
return (
<div className="App">
...
<SectionComponent />
</div>
);
}

Related

React.js Display a component with onClick event

I' m new to React and I'm building a simple React app that displays all the nations of the world on the screen and a small search bar that shows the data of the searched nation.
Here an image of the site
But I don't know how to show the country you want to click in the scrollbar.
Here the app.js code:
import React, { Component } from 'react';
import './App.css';
import NavBar from '../Components/NavBar';
import SideBar from './SideBar';
import CountryList from '../Components/SideBarComponents/CountryList';
import Scroll from '../Components/SideBarComponents/Scroll';
import Main from './Main';
import SearchCountry from '../Components/MainComponents/SearchCountry';
import SearchedCountry from '../Components/MainComponents/SearchedCountry';
import Datas from '../Components/MainComponents/Datas';
class App extends Component {
constructor() {
super();
this.state = {
nations: [],
searchField: '',
button: false
}
}
onSearchChange = (event) => {
this.setState({searchField: event.target.value});
console.log(this.state.searchField)
}
onClickChange = () => {
this.setState(prevsState => ({
button: true
}))
}
render() {
const {nations, searchField, button, searchMemory} = this.state;
const searchedNation = nations.filter(nation => {
if(button) {
return nation.name.toLowerCase().includes(searchField.toLowerCase())
}
});
return (
<div>
<div>
<NavBar/>
</div>
<Main>
<div className='backgr-img'>
<SearchCountry searchChange={this.onSearchChange} clickChange={this.onClickChange}/>
<SearchedCountry nations={searchedNation}/>
</div>
<Datas nations={searchedNation}/>
</Main>
<SideBar>
<Scroll className='scroll'>
<CountryList nations={nations} clickFunc/>
</Scroll>
</SideBar>
</div>
);
}
componentDidMount() {
fetch('https://restcountries.eu/rest/v2/all')
.then(response => response.json())
.then(x => this.setState({nations: x}));
}
componentDidUpdate() {
this.state.button = false;
}
}
export default App;
The countryList:
import React from 'react';
import Images from './Images';
const CountryList = ({nations, clickFunc}) => {
return (
<div className='container' style={{display: 'grid', gridTemplateColumns: 'repeat(auto-fill, minmax(115px, 3fr))'}}>
{
nations.map((country, i) => {
return (
<Images
key={country.numericCode}
name={country.name}
flag={country.flag}
clickChange={clickFunc}
/>
);
})
}
</div>
)
}
export default CountryList;
And the images.js:
import React from 'react';
import './images.css'
const Images = ({name, capital, region, population, flag, numericCode, clickChange}) => {
return (
<div className='hover bg-navy pa2 ma1 tc w10' onClick={clickChange = () => name}>
<img alt='flag' src={flag} />
<div>
<h6 className='ma0 white'>{name}</h6>
{capital}
{region}
{population}
{numericCode}
</div>
</div>
);
}
export default Images;
I had thought of using the onClick event on the single nation that was going to return the name of the clicked nation. After that I would have entered the name in the searchField and set the button to true in order to run the searchedNation function.
I thank anyone who gives me an answer in advance.
To keep the actual structure, you can try using onClickChange in Images:
onClickChange = (newName = null) => {
if(newName) {
this.setState(prevsState => ({
searchField: newName
}))
}
// old code continues
this.setState(prevsState => ({
button: true
}))
}
then in onClick of Images you call:
onClick={() => {clickChange(name)}}
Or you can try as well use react hooks (but this will require some refactoring) cause you'll need to change a property from a parent component.
With that you can use useState hook to change the value from parent component (from Images to App):
const [searchField, setSearchField] = useState('');
Then you pass setSearchField to images as props and changes the searchField value when Images is clicked:
onClick={() => {
clickChange()
setSearchField(name)
}}

reading value from custom element while submitting in react js

I have custom component which I am importing in my another Component as a Element tag. My custom Component consist of dropdown values. I want to read value the value of in my element tag when I submit my form
custom component :
import React, { useState, useMemo } from 'react'
import Select from 'react-select'
import countryList from 'react-select-country-list'
function CountrySelector() {
const [value, setValue] = useState('')
const options = useMemo(() => countryList().getData(), [])
const changeHandler = value => {
setValue(value)
}
return <Select options={options} value={value} onChange={changeHandler} />
}
export default CountrySelector
i want to use that custom component country selector values on my submit button:
main component:
import react from 'react';
import CountrySelector from '../../helpers/CountrySelector';
import IdType from '../../helpers/IdType';
import ProofOfAddress from '../../helpers/ProofOfAddress';
const submitForm=(e)=>{
//debugger;
e.preventDefault();
console.warn(e.target)
};
const IdentityVerification = (props) => {
const saveUser=(e)=>{
console.warn({e});
}
return (
<form onSubmit={submitForm} >
<div className='app'>
<label >Choose Issuing Country/region</label>
<CountrySelector/>
<label >Select ID Type</label>
<IdType/>
<label >Proof of Address</label>
<ProofOfAddress/>
</div>
<div className="form-actions">
<button >Submit</button>
</div>
</form>
);
};
export default IdentityVerification;
how can i read values?
The normal way to handle this would be to move the state and your changeHandler function into the parent component and pass the handler down to the child as a prop.
const IdentityVerification = (props) => {
const [value, setValue] = useState('')
const changeHandler = value => {
setValue(value)
}
return (
// ...
<CountrySelector onChange={changeHandler}/>
// ...
);
and in your child:
function CountrySelector({changeHandler}) {
// ....
return <Select options={options} value={value} onChange={changeHandler} />
}

Reducer/Context Api

So I have a Context created with reducer. In reducer I have some logic, that in theory should work. I have Show Component that is iterating the data from data.js and has a button.I also have a windows Component that is iterating the data. Anyway the problem is that when I click on button in Show Component it should remove the item/id of data.js in Windows Component and in Show Component, but when I click on it nothing happens. I would be very grateful if someone could help me. Kind regards
App.js
const App =()=>{
const[isShowlOpen, setIsShowOpen]=React.useState(false)
const Show = useRef(null)
function openShow(){
setIsShowOpen(true)
}
function closeShowl(){
setIsShowOpen(false)
}
const handleShow =(e)=>{
if(show.current&& !showl.current.contains(e.target)){
closeShow()
}
}
useEffect(()=>{
document.addEventListener('click',handleShow)
return () =>{
document.removeEventListener('click', handleShow)
}
},[])
return (
<div>
<div ref={show}>
<img className='taskbar__iconsRight' onClick={() =>
setIsShowOpen(!isShowOpen)}
src="https://winaero.com/blog/wp-content/uploads/2017/07/Control-
-icon.png"/>
{isShowOpen ? <Show closeShow={closeShow} />: null}
</div>
)
}
```Context```
import React, { useState, useContext, useReducer, useEffect } from 'react'
import {windowsIcons} from './data'
import reducer from './reducer'
const AppContext = React.createContext()
const initialState = {
icons: windowsIcons
}
const AppProvider = ({ children }) => {
const [state, dispatch] = useReducer(reducer, initialState)
const remove = (id) => {
dispatch({ type: 'REMOVE', payload: id })
}
return (
<AppContext.Provider
value={{
...state,
remove,
}}
>
{children}
</AppContext.Provider>
)
}
export const useGlobalContext = () => {
return useContext(AppContext)
}
export { AppContext, AppProvider }
reducer.js
const reducer = (state, action) => {
if (action.type === 'REMOVE') {
return {
...state,
icons: state.icons.filter((windowsIcons) => windowsIcons.id !== action.payload),
}
}
}
export default reducer
``data.js```
export const windowsIcons =[
{
id:15,
url:"something/",
name:"yes",
img:"/images/icons/crud.png",
},
{
id:16,
url:"something/",
name:"nine",
img:"/images/icons/stermm.png",
},
{
id:17,
url:"domething/",
name:"ten",
img:"/images/icons/ll.png",
},
{
id:18,
url:"whatever",
name:"twenty",
img:"/images/icons/icons848.png",
},
{
id:19,
url:"hello",
name:"yeaa",
img:"/images/icons/icons8-96.png",
},
]
``` Show Component```
import React from 'react'
import { useGlobalContext } from '../../context'
import WindowsIcons from '../../WindowsIcons/WindowsIcons'
const Show = () => {
const { remove, } = useGlobalContext()
return (
<div className='control'>
{windowsIcons.map((unin)=>{
const { name, img, id} = unin
return (
<li className='control' key ={id}>
<div className='img__text'>
<img className='control__Img' src={img} />
<h4 className='control__name'>{name}</h4>
</div>
<button className='unin__button' onClick={() => remove(id)} >remove</button>
</li> )
</div>
)
}
export default Show
import React from 'react'
import {windowsIcons} from "../data"
import './WindowsIcons.css'
const WindowsIcons = ({id, url, img, name}) => {
return (
<>
{windowsIcons.map((icons)=>{
const {id, name , img ,url} =icons
return(
<div className='windows__icon' >
<li className='windows__list' key={id}>
<a href={url}>
<img className='windows__image' src={img}/>
<h4 className='windows__text'>{name}</h4>
</a>
</li>
</div>
)
})}
</>
)
}
Issue
In the reducer you are setting the initial state to your data list.
This is all correct.
However, then in your Show component you are directly importing windowsIcons and looping over it to render. So you are no longer looping over the state the reducer is handling. If the state changes, you won't see it.
Solution
In your Show component instead loop over the state that you have in the reducer:
const { remove, icons } = useGlobalContext()
{icons.map((unin) => {
// Render stuff
}
Now if you click remove it will modify the internal state and the icons variable will get updated.
Codesandbox working example

React useContext not triggering re-rendering

I have a button in a child component AddMore.js which adds more items on a list, but it's not re-rendering the other child component that renders that list Pig.js. I thought my setup would work because I set up the context and provider in a similar fashion using the NextJS framework. Here's the source code, using codesandbox, or you can look at the code below.
App.js
import React, { useState } from "react";
import "./App.css";
import PigContext from "./store/PigContext";
import Pig from "./Pig";
import AddMore from "./AddMore";
function App() {
const [data, setData] = useState([
{ pig: "oink" },
{ pig: "bork" },
{ pig: "oinkoink" }
]);
return (
<div className="App">
<header className="App-header">
<PigContext.Provider value={{ data, setData }}>
<Pig />
<AddMore />
</PigContext.Provider>
</header>
</div>
);
}
export default App;
PigContext.js
import { createContext } from "react";
const PigContext = createContext(undefined);
export default PigContext;
Pig.js
import React, { useContext } from "react";
import PigContext from "./store/PigContext";
const Pig = () => {
const { data } = useContext(PigContext);
return (
<div>
{data.map(({ pig }, idx) => (
<div key={idx}>{pig}</div>
))}
</div>
);
};
export default Pig;
AddMore.js
import React, { useContext } from "react";
import PigContext from "./store/PigContext";
export default function AddMore() {
const { setData } = useContext(PigContext);
return (
<div>
<button
onClick={() =>
setData(prev => {
prev.push({ pig: "zoink" });
console.log(prev);
return prev;
})
}
>
add pig question
</button>
</div>
);
}
You are mutating the state object by calling push, you should return a new array instead in order to get the component updated.
AddMore.js:
...
<button
onClick={() =>
setData(prev => (
[...prev, ({ pig: "zoink" })]
))
}
>
...
It is because you are a mutating state in setData using push. using a method that doesn't mutate state like concat can fix the issue. Your AddMore function will look like
function AddMore() {
const { setData } = useContext(PigContext);
return (
<div>
<button onClick={() => setData(prev => prev.concat({ pig: "zoink" }))}>
add pig question
</button>
</div>
);
}

React, looping through object, displaying list, and grabbing that object when selected

Edit:
I have made it work by getting the target.value from the list, passing that to firstComp.js. Where the onChange method is below, but is this the best practice?
onchange = (e) =>{
let name= e.target.value
let currentName= this.state.foodData.map((item)=>{
if(name==item.name){
console.log(item)
}
})
}
Im doing stephen griders course on react, and trying to implement what he teaches on a personal project, but I cant wrap my head around it.
I have a list, that is looping through an array of objects. When I pick something from that list, I want it to update the state with that object.
The Layout..
DropDownList.js = return a drop down list with all the ingredient names in foodData
DropDownItem = loops through the foodData, returning an option value for each one.
foodData.js = db that looks something like this..
let foodData=[
{"name":"MCT Oil", "brand":"Keppi Keto", "servings":1}
{"name":"Chunky Peanut Butter", "brand":"Skippy"}
]
firstComp.js
import React, { Component} from 'react'
import ReactDOM from 'react-dom'
import foodData from './food/foodData.js'
import DropDownList from './DropDownList.js'
class Layout extends Component {
constructor () {
super()
this.state = {
foodData:foodData,
selectedFood:''
}
this.onchange =this.onchange.bind(this)
}
onchange = (e) =>{
console.log(e.target.value)
}
render () {
return (<div className='home'>
<DropDownList foodData={this.state.foodData} onchange={this.onchange} />
</div>)
}
}
const app = document.getElementById('app')
ReactDOM.render(<Layout />, app)
DropDownList.js
import React from 'react'
import DropDownItem from './DropDownItem.js'
const DropDownList = (props) =>{
****let textInput = React.createRef();**** //REFS
const ingredientItems = props.foodData.map((ingredient, i)=>{
return<DropDownItem key={i} ingredient={ingredient} **ref={textInput}**//REFS />
})
return(
<select value={ingredientItems.name} onChange ={ (e) => props.onchange(e)} >
{ingredientItems}
</select>
)
}
export default DropDownList;
DropDownItem.js
import React from 'react'
const DropDownItem = ({ingredient}) =>{
const itemName = ingredient.name
return(<option value={itemName}>{itemName}</option> )
}
export default DropDownItem;
You don't need to use refs here. You can use some kind of handleChange function in your DropdownList component and send back the value to parent component.
const DropDownList = (props) => {
const handleChange = e => props.onchange( e.target.value );
const ingredientItems = props.foodData.map((ingredient) =>
<DropDownItem key={ingredient.name} ingredient={ingredient} /> );
return (
<select onChange={handleChange}>
{ingredientItems}
</select>
)
}
and in the parent component:
class Layout extends Component {
state = {
foodData,
selectedFood: foodData[0]
};
onchange = name =>
this.setState({
selectedFood: foodData.filter(food => food.name === name)[0]
});
render() {
return (<div className='home'>
<DropDownList foodData={this.state.foodData} onchange={this.onchange} />
</div>)
}
}
or without struggling with values and filter, we can use e.target.options.selectedIndex as OP finds out him/herself :) This is a cleaner way and works as this (only related parts) :
DropdownList.js
const handleChange = e => props.onchange( e.target.options.selectedIndex);
Layout.js
onchange = index => this.setState({ selectedFood: foodData[index] });

Categories