How does one change props value to a different variable? - javascript

The problem is I can't access the value of actuve
or change it? Is this because of the fact that
props are immutable? and if so I should I
create a separate variable for each
EventInLife element/component?
import React from 'react';
import styled,{ css } from 'styled-components';
const EventInLife = (props) => {
/*
adds the active theme to each element(example: if active then the styles
would be barbar and barbaractive if not they would be barbar and barbar)
*/
return (
<div className={`barbar barbar${ props.actuve }`}>
<div className={`square square${ props.actuve }`}></div>
<div className={`heading heading${ props.actuve }`}>{props.heading}</div>
<div className={`date date${ props.actuve }`}>{props.date}</div>
</div>
);
}
function App() {
//Manages which value is active
var lastactive=0;
function bruh(t,n){
document.getElementsByName(t)[lastactive].actuve='';
document.getElementsByName(t)[n].actuve = 'active';
lastactive=n;
}
return(
<EventInLife heading={'Top'} date={'145'} actuve={'active'} onClick={()=>bruh('EventInLife',0)}/>
<EventInLife heading={'trop'} date={'456456'} actuve={''} onClick={()=>bruh('EventInLife',1)}/>
<EventInLife heading={'brop'} date={'45646'} actuve={''} onClick={()=>bruh('EventInLife',2)}/>
<EventInLife heading={'crop'} date={'45646'} actuve={''} onClick={()=>bruh('EventInLife',3)}/>
<EventInLife heading={'wrop'} date={'145645'} actuve={''} onClick={()=>bruh('EventInLife',4)}/>
);
}
/*the css style names (i made them only overide what they needed to)*/
.barbar{}
.barbaractive{}
.squareactive{}
.squareactive{}
.headingactive{}
.headingactive{}
.dateactive{}
.dateactive{}
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.6.3/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.6.3/umd/react-dom.production.min.js"></script>

Yes props are immutable, think of them as arguments being passed to a function. Any values that will change over the lifecycle of a component should be stored in the state of the component, changing the state will cause the component to rerender and thus display the changes on the DOM if the state is utilized correctly. Here is a simple example where each component has a button that triggers the active state to toggle thus triggering a rerender of the component which causes the classes variable to be recomputed therefore changing the class names passed to the div element. I have made the assumption each element is initially false.
import React , {useState} from 'react';
const RandComponent = (props) => {
const [isActive , setIsActive] = useState(false);
const classes = isActive ? 'bar barActive' : 'bar';
return(
<>
<div className={classes}>hi</div>
<button onClick={() => setIsActive(!isActive)}>{isActive ? 'make me inactive' : 'make me active'}</button>
</>
);
}

Related

Exporting a variable set in a function to another component React

I have some variables that are set within a function in one component of my react application that I need to reuse in other components.
I set the variables in component 1 like so (this is a much simplified version but captures the structure)
export default function Example() {
const [market, setMarket] = useState('');
return (
<button onClick={setMarket('1')}>Click 1</button>
<button onClick={setMarket('2')}>Click 2</button>
<button onClick={setMarket('3')}>Click 3</button> )}
How can I export the 'market' variable specifically, so that I can import it into another component (in a separate jsx file) and render as necessary. I know that I can just import the whole component, or set a variable outside of this function in component 1 and export it but I do not know how I would then conditionally set it based on which button is clicked.
Thank you
Hey #Milo there are different ways to use state value in another component.
First is props-
Create a component that passes values like-
const passValue = () => {
const [ value, setValue ] = useState("")
return (
)
}
While in the second component we get the value like-
const SecondComponent = ({value})=>{
return(
<div>
{value}
</div>
)
}
While Second method is to pass value using state and get it by useLocation in another component-
First Component like-
const FirstComponent = () =>{
return(
<div>
<Link to="/secondpage" state={{value:yourValue/state}}>Click Here</Link>
</div>
)
}
Second Component Like-
const Second Component = () => {
const {state} = useLocation()
return(
<div>{state}</div>
)
}
Hope these solution helps to solve your problem. If you still facing issue lemme know, i will help you.
Thanks

Variable passed trough state in Link can't update

I am updating my theme in my App per useState. This is passed to Topbar-Component per prop. console.log() gets triggered every time it changes. From Topbar theme is passed into a link to AboutMe-Copmponent as state, which works, but when i now change the state of theme it only updates in Topbar. I even tried Useeffect. Only when I refresh the site the change is noticed. I read hours about this but I cant solve it somehow.
AppComponent (not all code just the necessary):
function App() {
const [theme, setTheme] = useState('dark')
return (
<Topbar theme={theme}></Topbar>
<ToggleButton variant='light' onClick={() => setTheme('light')}>Light</ToggleButton>
<ToggleButton variant='dark' onClick={() => setTheme('dark')}>Dark</ToggleButton>
TopbarComponent:
export default function Topbar({theme}) {
console.log('Topbar',theme)
React.useEffect(()=>{
console.log('changed')
},[theme])
Output when I press the buttons:
Topbar light
changed
Topbar dark
changed
AboutMeComponent:
export default function AboutMe() {
const location = useLocation()
console.log(location.state)
React.useEffect(() => {
console.log('About-Me',location.state)
},[location])
Initial output:
dark
About-Me dark
When I now press the other Button I only get the Topbar Output
Only when refreshing I get the AboutMe Outputs again.
PS
The theme is changed anyway from dark to light but i need this state to change fonts etc.
I would suggest sticking with documentation's recommendation which is to use useContext for very this example of setting theme using context.
Check out: https://beta.reactjs.org/apis/react/useContext
Usage : Passing data deeply into the tree
import { useContext } from 'react';
function Button() {
const theme = useContext(ThemeContext);
useContext returns the context value for the context you passed. To determine the context value, React searches the component tree and finds the closest context provider above for that particular context.
To pass context to a Button, wrap it or one of its parent components into the corresponding context provider:
function MyPage() {
return (
<ThemeContext.Provider value="dark">
<Form />
</ThemeContext.Provider>
);
}
function Form() {
// ... renders buttons inside ...
}
It doesn’t matter how many layers of components there are between the provider and the Button. When a Button anywhere inside of Form calls useContext(ThemeContext), it will receive "dark" as the value.
I have it working now with the useContext hook. Thank you i somehow forgot about it.
App:
export const ThemeContext = React.createContext()
function App() {
const [theme, setTheme] = useState('black')
console.log(theme)
return (
<ThemeContext.Provider value={{backgroundColor:theme}}>
<BrowserRouter>
<div className='App' id={theme}>
<Topbar/>
<div className="position-absolute top-0 start-0">
<ToggleButton variant='light' onClick={() => setTheme('white')}>Light</ToggleButton>
<ToggleButton variant='dark' onClick={() => setTheme('black')}>Dark</ToggleButton>
</div>
Topbar:
export default function Topbar() {
const {user,logout} = UserAuth()
const [error, setError] = useState('')
const navigate = useNavigate()
const style = useContext(ThemeContext)
console.log(style)
AboutMe:
export default function AboutMe() {
const style = useContext(ThemeContext)
console.log(style)
return (
<>
<div className='d-flex' style={style}>
I had to move my Routing from Index.js to App.js because it had to be wrapped in the Context provider, but now my theme gets passed into every single component.

I want to only a single component update on state change

I want to update only a single element when using setState in a function
import { useState } from "react";
export default function App(){
const [state, setState] = useState("foo");
return(
<Component1/>
<Component2/>
<Component3/>
);
}
I need some way of updating one some of those elements, but not all.
In functional components, you can wrap your component with React.memo. With this way, React will memorize the component structure and on next render, if the props still the same, React does not render again and use the memorized one. For more information, https://reactjs.org/docs/react-api.html#reactmemo
Basically wrap with React.memo. Below code, when state1 change, Component2's render count won't increase because its props stays same. But when state2 change, both of them will render.
export const Component2 = React.memo(({ state2 }) => {
const renderCount = useRef(0);
renderCount.current = renderCount.current + 1;
return (
<div style={{ margin: 10 }}>
Component2(with React.memo): <b>Rendered:</b> {renderCount.current} times,{" "}
<b>State2:</b> {state2}
</div>
);
});
export default function App() {
const [state1, setState1] = useState(1);
const [state2, setState2] = useState(1);
return (
<div className="App">
<div onClick={() => setState1((state) => state + 1)}>
Click to change state1
</div>
<div onClick={() => setState2((state) => state + 1)}>
Click to change state2
</div>
<Component1 state1={state1} />
<Component2 state2={state2} />
</div>
);
}
I created a sandbox for you to play. Click the buttons and see React.memo in action. https://codesandbox.io/s/xenodochial-sound-gug2x?file=/src/App.js:872-1753
Also, with Class Components, you can use PureComponents for the same purpose. https://reactjs.org/docs/react-api.html#reactpurecomponent

React - Change state in child component from another child component

I need to change the state in one child component when a button is clicked in another child component. Both the childs have the same parent component.
import React from "react":
import A from "...";
import B from "...";
class App extends React.Component {
render() {
<div>
<A />
<B />
</div>
}
}
In this example, when a button in component A is pressed, the state in component B needs to be changed.
This application sounds like the perfect use case for "Lifting State Up", i.e. holding the main state in the parent component. Then you basically just pass down handlers (to change the parent state) to component A (this becomes the button's onClick handler), then pass down the state you want to show to component B.
When you click the button, the setState is called in the parent component, which automatically re-renders all children whose props change (including component B).
Here's more detailed info: https://reactjs.org/docs/lifting-state-up.html
EDIT: The reply below reminded me that I should probably add some code to illustrate - but I've made a few changes that simplify things.
import React, {useState} from "react":
import A from "...";
import B from "...";
const App = () => {
const [show, setShow] = useState(false);
function handleToggle() {
// Decouple the implementation of the parent state change from the child
// Pass a function to change the state (async/batching reasons)
setShow(show => !show);
}
return (
<div>
<A show={show} onToggle={handleToggle} />
<B show={show} onToggle={handleToggle} />
</div>
)
}
const A = ({show, onToggle}) => (
<div>
<p>show: {show}</p>
<button onClick={onToggle}>
toggle
</button>
</div>
)
const B = ({show, onToggle}) => (
<div>
<p>show: {show}</p>
<button onClick={onToggle}>
toggle
</button>
</div>
)
So basically we don't care how the state in the parent is changed. We just know that when the button in the child component is clicked, we want to trigger that change. So all we really have to do is call the function passed down via props - we don't have to pass any params.
The parent will then handle any clicks inside the handleToggle function, and you can change this implementation in the future without the child knowing anything. Perhaps you want to change to use mobx to handle state, or run some other code before finally changing the state. Since both are decoupled, you're all good! I've also adjusted setShow to use a function (benefits described here: https://reactjs.org/docs/state-and-lifecycle.html#state-updates-may-be-asynchronous).
A supplement to the answer above:
import React, {useState} from "react":
import A from "...";
import B from "...";
const App = () => {
const [show, setShow] = useState(false)
return (
<div>
<A show={show} setShow={setShow} />
<B show={show} setShow={setShow} />
</div>
)
}
const A = ({show, setShow}) => (
<div>
<p>show: {show}</p>
<button onClick={() => setShow(!show)}>
toggle
</button>
</div>
)
const B = ({show, setShow}) => (
<div>
<p>show: {show}</p>
<button onClick={() => setShow(!show)}>
toggle
</button>
</div>
)

Access props/attributes from child component with hooks

I'm trying to create a feature to easily hide/show all items (subcomponents). By using useState I am able to set whether or not all items are hidden/shown. By using useEffect I am able to toggle items that are hidden/shown. I'm having issues accessing the props in the subcomponent to determine whether or not a an item has already been expanded. I wish I could explain this better, but hopefully this coding example will paint a better picture.
index.js
import React, { useState } from "react";
import ReactDOM from "react-dom";
import "semantic-ui-css/semantic.min.css";
import { Button } from "semantic-ui-react";
import Item from "./Item";
const Services = props => {
const [allExpanded, setAllExpanded] = useState(false);
return (
<>
<p>
<Button onClick={() => setAllExpanded(false)} content="Hide all" />
<Button onClick={() => setAllExpanded(true)} content="Show all" />
</p>
<p>
<Item expanded={allExpanded} />
<Item expanded={allExpanded} />
<Item expanded={allExpanded} />
</p>
</>
);
};
const rootElement = document.getElementById("root");
ReactDOM.render(<Services />, rootElement);
Item.js
import React, { useState, useEffect } from "react";
import { Accordion } from "semantic-ui-react";
const Item = props => {
const [expanded, setExpanded] = useState(props.expanded);
useEffect(() => {
setExpanded(props.expanded);
}, [props.expanded]);
return (
<Accordion styled>
<Accordion.Title
onClick={() => {
setExpanded(!expanded);
}}
>
<p>{expanded ? "- Hide Item" : "+ Show Item"}</p>
</Accordion.Title>
<Accordion.Content active={expanded}>Lorem ipsum...</Accordion.Content>
</Accordion>
);
};
export default Item;
CodeSandbox
To replicate my current bug, click any "+ Show Item", then click "Hide All". It will not hide everything, however clicking "Show All", then "Hide All" will hide everything.
You're facing this issue because your parent component actually has three possible states:
All expanded
All collapsed
Neither all expanded or collapsed
To reflect the third state, you could use null/undefined (and pass the setter down into your children components).
Updated example here: https://codesandbox.io/s/competent-villani-i6ggh
Since you are handling the expanded state of your accordions on the top level, I suggest you just pass down the expanded state and the 'toggler' to your items. index.js will handle the logic and your Item component will be presentational.
Here's a fork of your CodeSandbox.
It doesn't look great and probably the item state and toggling can (and probably should) be moved elsewhere (for example a separate reducer with the usage of the useReducer hook)
If you are planning to create these components dynamically, IMO this is the easiest way to go.
If you still want to go your way, you can refactor your Item to a class component and use Refs to get their current state, however I not recommend this approach.
Hope this helps!
Here's a codeandsandbox, forked from yours:
https://codesandbox.io/s/competent-wildflower-n0hb8
I changed it so that instead of having something like this:
let [allExpanded, setAllExpanded] = useState(true)
You have something like this:
let [whichExpanded, setWhichExpanded] = useState({0: true, 1:true, 2: true})
Then, on for your callback to expand/collapse all buttons, you have this:
<button onClick=()=>{
let newState = {}
for(let order in whichEpanded){
newState[order] = false //change every key to false
}
setAllExpanded(newState)
}> hide all</button>
Then, I passed down an "order" prop to your items. The "order" prop is used as an argument to a callback function that I pass down, so when you click on each item, it updates the whichExpanded state, to toggle the visibility of just that one item.
// pass this to eac of the items:
const setIndividualItemExpanded = order => {
let newItemsExpandedState = { ...itemsExpanded };
newItemsExpandedState[order] = !newItemsExpandedState[order];
setItemsExpanded(newItemsExpandedState);
};
Each item component:
<Item
expanded={itemsExpanded[0]} //is reading from the state
order={0}
setExpanded={setIndividualItemExpanded}
/>
Then, you can remove the useState from the rendered component and just update with the "setExpanded" prop
(See complete code in codesandbox pasted at top)

Categories

Resources