Ok. I have the app.js (which will render all components on my screen) and inside this file i embeded two other js files (components). The first one is basically a button that adds one more word to an array. It goes something like this:
import { useState } from "react";
function DescriptionSector() {
const [title, setTitle] = useState([]);
return (
<button onClick={() => setTitle([...title, "New title defined"])}>add word</button>
)
This first component is working just fine as I used console.log to test it.
THe problem is with the second part.
The second part consists basically of a list that renders the array create on the first part and here's where i having trouble.
function FinancialResume({ title }) {
return (
<ul>
{title.map(e => {
return (
<li>{e}</li>
)
})}
</ul>
)
}
I tried using the props object to send the updated array like this:
import { useState } from "react";
function DescriptionSector() {
const [title, setTitle] = useState([]);
return (
<button
onClick={() => {
setTitle([...title, "New title defined"]);
FinancialResume(title);
}}
>
add word
</button>
)
BUT IT DIDNT WORKED
EDIT: here's my app.js
import DescriptionSector from "./Components/descriptionSector/description";
import FinancialResume from "./Components/financialresume/financialresume";
function App() {
return (
<div className="App">
<div className="user-body__leftSector">
<DescriptionSector />
</div>
<div className="user-body__rightSector">
<FinancialResume />
</div>
</div>
)}
export default App;
Assuming you want the changes made in DescriptionSector to be rendered by FinancialResume, one way you can do that with React is by passing props from a shared parent.
Let App control the title state. It can pass the setter down to DescriptionSector and the value down to FinancialResume.
React states are reactive to changes. App and FinancialResume will re-render when title changes without you having to call any functions.
function App() {
const [title, setTitle] = useState([]);
return (
<div className="App">
<div className="user-body__leftSector">
<DescriptionSector setTitle={setTitle} />
</div>
<div className="user-body__rightSector">
<FinancialResume title={title} />
</div>
</div>
);
}
function DescriptionSector({ setTitle }) {
return (
<button
onClick={() => {
setTitle((title) => [...title, "New title defined"]);
}}
>
add word
</button>
);
}
function FinancialResume({ title }) {
return (
<ul>
{title.map((e, i) => {
return <li key={i}>{e}</li>;
})}
</ul>
);
}
There are of course other ways to manage shared state such as Context and state stores like Redux Toolkit but those are more advanced topics.
Related
I have two components "search" and "Maindata". I am passing the input value from the search component to maindata component where I want to replace the city attribute with the input value(location) in API. but the browser display went blank and the console give an undefined 'city' error, etc. I got stuck in this problem if anyone has a solution?
Here "search" component;
import React , {useState} from "react";
import Maindata from "./Maindata";
import "../Componentstyle/search.css";
export default function Search() {
const [location, setLocation] = useState();
<Maindata city={location}/>
return (
<div className="main">
<nav className="istclass">
<form className="form">
<div className="search">
<input
value={location}
placeholder="search city"
className="searchbox"
onChange={(e)=>setLocation(e.target.value)}
/>
<button className="nd" onClick={(e)=>setLocation(e.target.value)}>
Submit
</button>
</div>
</form>
</nav>
</div>
);
}
Here "Maindata" component;
import React, { useState, useEffect } from "react";
import "../Componentstyle/Main.css";
export default function Maindata(props) {
const [data, setData] = useState(null);
let city = console.log(props.city);
let weather = async () => {
const key = "1ab6ef20384db1d7d9d205d609f7eef0";
await fetch(
`https://api.openweathermap.org/data/2.5/weather?q=${city}&appid=${key}&units=metric&formatted=0`
)
.then((response) => response.json())
.then((actualData) => setData(actualData));
};
useEffect(() => {
weather();
}, []);
if (!data) {
return <div>Loading...</div>;
}
const link = `http://openweathermap.org/img/w/${data.weather[0].icon}.png`;
return (
<div className="maindata">
<div className="city">{data.name}</div>
<div className="temp">{data.main.temp} C</div>
<div className="icon">
<img src={link} alt="not found" />{" "}
</div>
<div className="feel">feels Like {data.main.feels_like} C</div>
<div className="wind">Wind {data.wind.speed} Km/hr</div>
<div className="cloudy">{data.weather[0].main}</div>
<div className="humidity">humidity {data.main.humidity}%</div>
<div className="sunrise">
sunrise :- {new Date(data.sys.sunrise * 1000).toUTCString()}{" "}
</div>
<div className="sunset">
sunset :- {new Date(data.sys.sunset * 1000).toUTCString()}
</div>
</div>
);
}
<Maindata city={location}/>
keep this line of code inside the return
In your example, there is no meaningful connection between the Search and Maindata components. Meaning Maindata component will not get rendered on the page because it is not in the return statement of the Search component.
The Maindata component as below, is in JSX format, when you use JSX in your code in React, under the hood, React.createElement() method is being called.
Each call to React.createElement returns an object describing what to render to that part of the page. So it makes sense to put the Maindata component in the return statement. That is responsible for rendering the HTML elements from that component when you're loading a page containing that component.
<Maindata city={location}/> // is JSX and should be in the return statement to get rendered on the page and showing the right location
let me explain my question.
I would like to create expanding flex cards, here is the exemple on codepen : https://codepen.io/z-/pen/OBPJKK
and here is my code for each button :
basically I have a component which is called HomeButtons that generates every flex cards. Inside this component I have a smaller component called readMore. In this component I have a useState that allows me to toggle individually each button to add or retreive an active class. If the active class is present, that means that the selected button must expand and the other ones must shrink.
What I would like to do is to access the readMore state ouside of the readMore subcomponent. That way I could write a function to remove the active class from a card if the user clicks on another card like so :
function setToUnactive() {
if (readMore(true)) {
readMore(false)}
}
My question is how can I get the state of readMore outside of the readMore subcomponent ? Do I need to use useContext ? Because that seems very simple to do but I tried a lot of things and nothing works. Can I pass the state readMore as a prop of the component ReadMore ? Thank you !
import React, { useState } from 'react';
import '../style/catalogue.scss';
import collectionsItems from '../Components/collectionsItemsData';
import { Link } from "react-router-dom";
const HomeButtons = ({}) => {
function ReadMore() {
const [readMore, setReadMore] = useState(false)
function toggleSetReadMore() {
setReadMore(!readMore)
}
return (
<p className='showmore' onClick={toggleSetReadMore} className={readMore ? "collection-item active" : "collection-item"}>TOGGLE BUTTON</p>
)
}
return <div>
{collectionsItems.map((collectionItem) => {
const { id, category, img } = collectionItem;
return < article key={id} >
<img className="item-img" src={img} alt=''/>
<ReadMore />
<Link to={`/${category}`} className="item-title">{category}</Link>
</article>
})}
</div>
}
export default HomeButtons;
First of all you need extract ReadMore component from function outside!
And for your problem you can lift state up(https://reactjs.org/docs/lifting-state-up.html). And since at the same time only one item can be opened you can do something like this:
function ReadMore({ isOpened, setOpened }) {
return (
<p
onClick={setOpened}
className={isOpened ? "collection-item active" : "collection-item"}
>
TOGGLE BUTTON
</p>
);
}
const HomeButtons = () => {
const [openedItemId, setOpenedItemId] = useState(null);
return (
<div>
{collectionsItems.map((collectionItem) => {
const { id, category, img } = collectionItem;
return (
<article key={id}>
<img className="item-img" src={img} alt="" />
<ReadMore
isOpened={openedItemId === id}
setOpened={() => setOpenedItemId(id)}
/>
<Link to={`/${category}`} className="item-title">
{category}
</Link>
</article>
);
})}
</div>
);
};
I have a modal when opened, display auth user data, currently, I can only open the modal on the dashboard, but I want to be able to render it from anywhere in my application. How do I achieve this?
Dashboard
const [visible, setVisible] = useState(false)
const trigerModal = ()=>(
<ModalCustom visible={visible} setVisible={setVisible}>
<form>
<>
<h3>Select an Account</h3>
<ul className="account">
{accounts && accounts.map((item, i) => (
<li key={item.id}>
<h3>{item.name}</h3>
<h3>{item.email}</h3>
<h3> {item.phone}</h3>
</li>
))}
</ul>
<br />
</>
</form>
</ModalCustom>
)
return(
<div>
{trigerModal()}
<button onClick={()=> setVisible(true)}>Open modal</button
</div>
)
Profile
how do trigger the modal from this component
Two statements will answer virtually every react question:
Don't mutate state (not applicable here)
Lift state up (this is the answer to your question).
Create a context - wrap your application in it, and have any component useContext to open a modal with whatever components you want it in:
export const ModalContext = React.createContext();
const ModalProvider = ({children}) => {
const [modalContent,setModalContent] = useState();
return (
<ModalContext.Provider value={
useMemo(() => ({
hide:() => setModalContent(),
open:setModalContent
}),[]
}>
{modalContent ? <Modal>{modalContent}</Modal> : null}
{children}
</ModalContext.Provider>
)
}
Wrap you application in the ModalProvider component so the context will be available to all your components:
const AdminDashboard = () => (
<ModalProvider>
<OtherComponents/>
</ModalProvider>
)
SomeLink, a component that is anywhere inside AdminDashboard can use React.useContext to access the state in ModalProvider
const SomeLink = () => {
const { show } = React.useContext(ModalContext);
return (
<button onClick={() => show(<SomeModalContent/>)}>Click to Open!</button>
)
}
If you want to access it from anywhere You need to use Global State (like Redux or Mobx)
If you want to control this from parent component you can use useRef
Basically I have a modal with a state in the parent component and I have a component that renders a list. When I open the modal, I dont want the list to re render every time because there can be hundreds of items in the list its too expensive. I only want the list to render when the dataSource prop changes.
I also want to try to avoid using useMemo if possible. Im thinking maybe move the modal to a different container, im not sure.
If someone can please help it would be much appreciated. Here is the link to sandbox: https://codesandbox.io/s/rerender-reactmemo-rz6ss?file=/src/App.js
Since you said you want to avoid React.memo, I think the best approach would be to move the <Modal /> component to another "module"
export default function App() {
return (
<>
<Another list={list} />
<List dataSource={list} />
</>
);
}
And inside <Another /> component you would have you <Modal />:
import React, { useState } from "react";
import { Modal } from "antd";
const Another = ({ list }) => {
const [showModal, setShowModal] = useState(false);
return (
<div>
<Modal
visible={showModal}
onCancel={() => setShowModal(false)}
onOk={() => {
list.push({ name: "drink" });
setShowModal(false);
}}
/>
<button onClick={() => setShowModal(true)}>Show Modal</button>
</div>
)
}
export default Another
Now the list don't rerender when you open the Modal
You can use React.memo, for more information about it please check reactmemo
const List = React.memo(({ dataSource, loading }) => {
console.log("render list");
return (
<div>
{dataSource.map((i) => {
return <div>{i.name}</div>;
})}
</div>
);
});
sandbox here
I'm currently looking for a way to access children state from a parent component that will handle API calls for the whole page.
The actual problem is the following:
Parent is the parent component that will render two Child components.
Each of the Child has a state that it is responsible for.
The "Kind of Submit Button" will have a "Kind of Submmit Action" (this is all quoted because this is not a form) and that should fire the function to provide access to the children state. Is there a way (some React feature) to do this without using <form> or without creating an intermediate parent component to hold all the state? I want each children to be responsible for its own state.
Code Sandbox with example of the code below
import React, { useState, useRef } from "react";
function ChildOne() {
const [childOneState, setChildOneState] = useState(false);
return (
<React.Fragment>
<h3>Child One</h3>
<p>My state is: {childOneState.toString()}</p>
<button onClick={() => setChildOneState(true)}>Change my state</button>
</React.Fragment>
);
}
function ChildTwo() {
const [childTwoState, setChildTwoState] = useState(false);
return (
<React.Fragment>
<h3>Child Two</h3>
<p>My state is: {childTwoState.toString()}</p>
<button onClick={() => setChildTwoState(true)}>Change my state</button>
</React.Fragment>
);
}
function Button(props) {
return (
<button onClick={props.kindOfSubmitAction}>Kind of Submit Button</button>
);
}
function Parent() {
const childOneState = useRef("i have no idea");
const childTwoState = useRef("ihave no idea");
function kindOfSubmitAction() {
console.log("This is the kindOfSubmit function!");
// This function would somehow get
// access to the children state and store them into the refs
return;
}
return (
<React.Fragment>
<h1>Iam Parent</h1>
<div>
<b>Child one state is: </b>
{childOneState.current}
</div>
<div>
<b>Child two state is: </b>
{childTwoState.current}{" "}
</div>
<Button kindOfSubmitAction={kindOfSubmitAction} />
<ChildOne />
<ChildTwo />
</React.Fragment>
);
}
export default Parent;
When several components need access to the same data, it's time for Lifting State Up.