How do I get a state variable from child to parent? - reactjs

I am learning React.
I have Component structure like this -
index.js
import React from "react";
import Button from "./Button/Button"
export default function Index() {
return (
<>
<Button />
<div>Value of flag in Index.js = {}</div>
</>
);
}
Button.js
import React, { useState } from "react";
import "./button.css";
export default function Button(props) {
const [flag, setFlag] = useState(true);
const clickHandler = () => {
setFlag(!flag);
};
return (
<div className="btn" onClick={clickHandler}>
Value of flag in Button.js = {flag.toString()}
</div>
);
}
My question is "How do I get flag value from Button.js to index.js" ? (child to parent).

1) You can lift state in parent component and pass state and handler as a prop to children.
Note: This is will work because you need flag in the JSX, But if you will pass event handler as a prop in the child component then you have to invoke the handler to get the value. So Either lift the state or use Redux
Live Demo
App.js
const App = () => {
const [flag, setFlag] = useState( true );
return (
<>
<Button flag={flag} setFlag={setFlag} />
<div>Value of flag in Index.js = { flag.toString() }</div>
</>
);
};
Button.js
export default function Button({ flag, setFlag }) {
const clickHandler = () => {
setFlag(oldFlag => !oldFlag);
};
return (
<div className="btn" onClick={clickHandler}>
Value of flag in Button.js = {flag.toString()}
</div>
);
}
2) You can pass handler as a prop in child component as shown in the Harsh Patel answer
3) You can use state management tool i.e. Redux.

You can send a value by method, refer to this:
index.js
import React from "react";
import Button from "./Button/Button"
export default function Index() {
let getFlagValue = (flag) => {
//here you'll get a flag value
console.log(flag)
}
return (
<>
<Button sendFlagValue=(getFlagValue)/>
<div>Value of flag in Index.js = {}</div>
</>
);
}
Button.js
import React, { useState } from "react";
import "./button.css";
export default function Button(sendFlagValue) {
const [flag, setFlag] = useState(true);
const clickHandler = () => {
setFlag(!flag);
sendFlagValue(flag)
};
return (
<div className="btn" onClick={clickHandler}>
Value of flag in Button.js = {flag.toString()}
</div>
);
}

There are two types state:
global state for all
private state for component
For starter, you must obey some policies, not try abuse state, otherwise you will have some troubles.
For global STATE, you can use Redux or dva or umijs.
For private STATE, you seems already known.

Related

Keep track of another components state

I have a bit of a basic React question that I am having trouble googling.
I have this component which is managing the state of maximize:
import React from 'react'
import { useState } from 'react';
import './Panel.scss'
import { AiFillExperiment, AiOutlineExpandAlt } from "react-icons/ai";
const Panel = ({title}) => {
const [maximize, setMaximize] = useState(false);
return (
<div className='panel'>
<AiFillExperiment />
<p>{title}</p>
<AiOutlineExpandAlt onClick={() => setMaximize(!maximize)} />
</div>
)
}
export default Panel
and this component that needs to be able to see the value of that state:
import './App.scss';
import { useEffect, useState } from 'react';
import ReactMarkdown from 'https://esm.sh/react-markdown#7'
import remarkBreaks from 'https://esm.sh/remark-breaks#3'
import Panel from './components/Panel'
function App() {
const [markdown, setMarkdown] = useState(``)
const placeholder =
`# Welcome to my React Markdown Previewer!
## This is a sub-heading...
### And here's some other cool stuff:
Here's some code, \`<div></div>\`, between 2 backticks.
\`\`\`
// this is multi-line code:
function anotherExample(firstLine, lastLine) {
if (firstLine == '\`\`\`' && lastLine == '\`\`\`') {
return multiLineCode;
}
}
\`\`\`
You can also make text **bold**... whoa!
Or _italic_.
Or... wait for it... **_both!_**
And feel free to go crazy ~~crossing stuff out~~.
There's also [links](https://www.freecodecamp.org), and
> Block Quotes!
And if you want to get really crazy, even tables:
Wild Header | Crazy Header | Another Header?
------------ | ------------- | -------------
Your content can | be here, and it | can be here....
And here. | Okay. | I think we get it.
- And of course there are lists.
- Some are bulleted.
- With different indentation levels.
- That look like this.
1. And there are numbered lists too.
1. Use just 1s if you want!
1. And last but not least, let's not forget embedded images:
![freeCodeCamp Logo](https://cdn.freecodecamp.org/testable-projects-fcc/images/fcc_secondary.svg)
`;
useEffect(() => {
setMarkdown(placeholder)
}, [placeholder])
return (
<div className="App">
{/* Editor Container */}
<div
className={'editor-container'}
>
<Panel title='Editor' />
<textarea id='editor' onChange={(e) => setMarkdown(e.target.value)} rows="" cols="">{placeholder}</textarea>
</div>
{/* Preview Container */}
<div className='preview-container'>
<Panel title='Preview' />
<div id='preview'>
<ReactMarkdown children={markdown} remarkPlugins={[remarkBreaks]} />
</div>
</div>
</div>
);
}
export default App;
How do I go about doing this? I realize I could have it all in one component, but I would like to know how to do it with two separate components.
Thanks in advance!
Through useState + props (less recommended)
You can do that by having that state in your App component and passing the setState as a property
const App = () => {
const [maximize, setMaximize] = useState(false);
const handleToggle = (newState) => {
setState(newState)
}
return (
<div>
<Panel toggleState={toggleState} maximize={maximize} />
</div>
)
}
And in your Panel component:
const Panel = ({toggleState, maximize}) => {
const handleToggle = () => {
toggleState(!maximize)
}
return (
<AiOutlineExpandAlt onClick={handleToggle} />
)
}
Through useContext hook
useContext allows you to store variables and access them on all child components within that context provider.
MaximizeProvider.js
import React, {useState, useContext} from "react";
//creating your contexts
const MaximizeContext = React.createContext();
const MaximizeUpdateContext = React.createContext();
// create a custom hook
export const useUpdate = () => {
return useContext(MaximizeUpdateContext)
}
export const useMaximize = () => {
return usecContext(MaximizeContext)
}
//creating your component that will wrap the child components
const MaximizeProvider = ({children}) => {
const [maximize, setMaximize] = useState(false)
// Your toggle to switch the state
const toggle = () => {
setMaximize(prevState => !prevState)
}
return (
<MaximizeContext.Provider value={maximize}>
<MaximizeUpdateContext.Provider value={toggle}>
{children}
</MaximizeUpdateContext.Provider>
</MaximizeContext.Provider>
)
}
export {MAximizeProvider}
Both providers allow you to access both the state and the setState
App.js
import React, {useState} from "react";
// your context component
import {MaximizeProvider} from "./MaximizeProvider";
// a button component
import {ButtonComponent} from "./ButtonComponent";
const App = () => {
return (
<>
<MaximizeProvider>
<ButtonComponent/>
</MaximizeProvider>
< />
);
}
export {App};
in the App, you are wrapping the elements that need your context.
as long as the elements and even children of children are in the wrap, it would have access to it the same way as in the button component.
ButtonComponent.js
import {useMaximize, useUpdate} from "./MaximizeProvider";
const ButtonComponent = () => {
const toggle = useUpdate();
const state = useMaximize()
return (
<button onClick={toggle}>Click</button>
);
}
export {ButtonComponent};
I hope this helps, I am not an expert, so there might be better ways to do it, but this seems to work for me.
Use redux or react context please,
props drilling is bad practice
https://reactjs.org/docs/context.html
https://redux.js.org/

React update function from child using context

How can i update a function from a child using context??
The context
import React from 'react';
const Fin_states = React.createContext({
title: "title_title"
});
export default Fin_states;
This is the main page , the aim is update function_() using set_function_(see the states const [function_,set_function_] ) , but doing it using the context in the List_of_list child.
import React , { useState ,useContext} from 'react';
import Input_list_panel from './Input_list_panel'
import Fin_states from './Financial_instrument_states'
export default function FinancialInstruments() {
//Attribs
const context = useContext(Fin_states);
const [value, set_value] = useState({title:"initial_title"})
const [function_, set_function_] = useState(() => (data) => console.log(data))
// set_function_ is going to be represented by update_function in the context provider.
return (
<>
<Fin_states.Provider value={{ data:value, update_value:set_value,update_function:set_function_}}>
<Input_list_panel/>
</Fin_states.Provider>
<button onClick={()=>function_("data")}>
Add item in child from parent
</button>
</>
);
}
This is the file where the function is going to be modified using useEffect in this line context.update_function(()=>updated_function)
import React , { useState , useEffect, forwardRef ,useRef,useImperativeHandle, useContext } from 'react';
import Fin_states from './Financial_instrument_states'
export default function List_of_list(props) {
const [list_of_list,set_list_of_list]= useState(["data_1","data_2"]);
const context = useContext(Fin_states);
const updated_function=(data)=>{
var new_item=[...list_of_list];
new_item.push(data)
set_list_of_list(new_item);
console.log(list_of_list)
}
useEffect(() => {
/*update_function represents at set_function_ to modify function_() state , see the context
provider in the parent FinancialInstruments*/
context.update_function(()=>updated_function)
}, [])
return (
<>
<Fin_states.Consumer>
{
(context)=>{
return(
<>
<button onClick={
()=>{
updated_function("data")
}
}>
Add item in child
</button>
</>
)
}
}
</Fin_states.Consumer>
</>
);
}
The problem is that when the state of function_() is updated in the main page (FinancialInstruments functional component) it does not use the same context than updated_function() in List_of_list.
How can i have the same result that i have in List_of_list using updated_function() but using function_() in FinancialInstruments?

React native typescript: usecontext functions not firing from inside child component

I have an issue when i try to use functions from a context inside a child component in a React native android app.
Below is my code for the context, and the form component im using it in (stripped down for brevity).
The "isFormOpen" object can be read no problem from inside any children that is wrapped in the provider, but when i try to call the "toggleForm" function from the same child component, it does nothing, no console errors either.
I have another context which is identical in structure and syntax except for vairable and function names etc, and that works perfectly, so im a bit confused as to why this does not work. I removed the other context, thinking there might be some type of conflict, but didnt solve it.
AccountContext.tsx
import React, { FC, createContext, useContext, useState } from 'react';
interface AccountContextType {
isFormOpen: boolean,
toggleForm: (toggle: boolean) => void
};
export const AccountContext = createContext<AccountContextType>({
isFormOpen: false,
toggleForm: () => null
});
export const AccountContextProvider: FC = props => {
const [formOpen, setFormOpen] = useState<boolean>(false);
const toggleForm = (toggle: boolean) => {
setFormOpen(toggle);
}
const value: AccountContextType = {
isFormOpen: formOpen,
toggleForm
}
return (
<AccountContext.Provider value={value}>
{props.children}
</AccountContext.Provider>
)
}
export const useAccountContext = () => useContext(AccountContext);
TrackUploadForm.js
import React from 'react';
import { SafeAreaView } from 'react-native';
import { Button } from 'react-native-paper';
import { useAccountContext } from '../contexts/AccountContext';
import { AccountContextProvider } from '../contexts/AccountContext';
const TrackUploadForm = () => {
const accountContext = useAccountContext();
return (
<AccountContextProvider>
<SafeAreaView>
<Button onPress={() => accountContext.toggleForm(false)} mode='outlined'>Cancel</Button>
</SafeAreaView>
</AccountContextProvider>
)
};
export default TrackUploadForm;
useAccountContext is called outside the provider
export default function App() {
return (
<AccountContextProvider>
<Content />
</AccountContextProvider>
);
}
const Content = () => {
const accountContext = useAccountContext();
return (
<div className="App">
<h1>{accountContext.isFormOpen ? "true" : "false"}</h1>
<Button onPress={() => accountContext.toggleForm(false)} mode='outlined'>Cancel</Button>
</div>
);
};
accountContext.toggleForm(false) <-- always false, change it to accountContext.toggleForm(!accountContext.isFormOpen)
Together we have
https://codesandbox.io/s/cranky-panini-yo129

React custom hooks not working as expected

I am trying to implement a very simple custom hook called useOpen that simply returns a boolean (isOpen). I want to show or hide some text in App.js based on isOpen state. Currently, nothing is being rendered and trying to console.log(isOpen) in App.js gives me undefined. Thanks in advance!
App.js
import React from 'react'
import useOpen from './CustomHooks/useOpen'
function App () {
const {isOpen} = useOpen;
return (
<div className='App'>
{isOpen && <p>isOpen</p>}
</div>
)
}
export default App
useOpen.js
import { useState } from 'react'
export default function useOpen() {
const [isOpen, setIsOpen] = useState(true)
return { isOpen }
}
You’re missing the parens on useOpen. Should be useOpen().
const {isOpen} = useOpen; // missing ()
const {isOpen} = useOpen();
Try to call function useOpen at the component.
you need to execute the hook to get its value
and you don't need to put it in a object to deconstruct it on the other side
App.js
import React from 'react'
import useOpen from './CustomHooks/useOpen'
function App () {
//const {isOpen} = useOpen;
const isOpen = useOpen();
return (
<div className='App'>
{isOpen && <p>isOpen</p>}
</div>
)
}
export default App
useOpen.js
import { useState } from 'react'
export default function useOpen() {
const [isOpen, setIsOpen] = useState(true)
//return { isOpen }
return isOpen
}

How do I get my React like button to increment when not liked, and then decrement when liked?

The state variable likes just continues to increment. I thought I have my onSelect function alternating the boolean value of liked, but apparently not.
Here's the error:
Error: Maximum update depth exceeded. This can happen when a component repeatedly calls
setState inside componentWillUpdate or componentDidUpdate. React limits the number of nested
updates to prevent infinite loops.
Here's App.js:
import React, { useState } from 'react';
import './App.css';
import LikeButton from './components/LikeButton';
function App() {
const [likes, updateLikes] = useState(23);
const [liked, updateLiked] = useState(false);
return (
<LikeButton
secret='like-button'
numLikes={likes}
liked={liked}
// status={liked}
onSelect={function clickLike(liked) { //This function is happening, but the else block
// never fires.
if (liked) {
updateLikes(likes + 1);
} else { updateLikes(likes - 1)
};
updateLiked(!liked);
}
}
/>
// onClick function here, or in LikeButton.js?
);
}
export default App;
And here's LikeButton.js:
import React from 'react';
import { FaThumbsUp } from 'react-icons/fa';
export default function LikeButton({secret, liked, numLikes, onSelect}) {
return (
<>
<FaThumbsUp />
<div key={secret} liked={liked} onClick={onSelect(liked)}>Like Button</div>
<p>{numLikes}</p>
</>
);
}
Issue
You invoke your onClick callback immediately, which updates state in the parent.
import React from 'react';
import { FaThumbsUp } from 'react-icons/fa';
export default function LikeButton({secret, liked, numLikes, onSelect}) {
return (
<>
<FaThumbsUp />
<div
key={secret}
liked={liked}
onClick={onSelect(liked)} // invoked right away
>
Like Button
</div>
<p>{numLikes}</p>
</>
);
}
Solution
Make it an anonymous inner function.
import React from 'react';
import { FaThumbsUp } from 'react-icons/fa';
export default function LikeButton({secret, liked, numLikes, onSelect}) {
return (
<>
<FaThumbsUp />
<div
key={secret}
liked={liked}
onClick={() => onSelect(liked)} // invoked when clicked
>
Like Button
</div>
<p>{numLikes}</p>
</>
);
}
Suggestion
You should also really use functional state updates when the next state values depend on current state values, i.e. when incrementing/decrementing counts or toggling a boolean. I also suggest to move the updateLikes into an useEffect hook to react to liked being toggled. This may make the logic about incrementing/decrementing a little clearer.
function App() {
const [likes, updateLikes] = useState(23);
const [liked, updateLiked] = useState(false);
useEffect(() => {
updateLikes(likes => likes + (liked ? 1 : -1));
}, [liked]);
return (
<LikeButton
secret='like-button'
numLikes={likes}
liked={liked}
// status={liked}
onSelect={function clickLike(liked) {
updateLiked(liked => !liked);
}}
/>
);
}

Resources