React / Functional component / Conditional render on callback / Not Working - javascript

Why this does not work ?
import React from 'react';
function Room() {
let check = null;
const ibegyouwork = () => {
check = <button>New button</button>;
}
return (
<div>
<button onClick={ibegyouwork}>Display my button now !!!!</button>
{check}
</div>
);
}
export default Room;
And this works fine ?
import React from 'react';
function Room() {
let check = null;
return (
<div>
<button>No need for this button because in this case the second button is auto-displayed</button>
{check}
</div>
);
}
export default Room;
Basically I try to render a component based on a condition. This is a very basic example. But what I have is very similar. If you wonder why I need to update the check variable inside that function is because in my example I have a callback function there where I receive an ID which I need to use in that new component.
The example that I provided to you is basically a button and I want to show another one when I press on this one.
I am new to React and despite I searched in the past 2 hours for a solution I couldn't find anything to address this issue.
Any tips are highly appreciated !

Your component has no idea that something has changed when you click the button. You will need to use state in order to inform React that a rerender is required:
import React, {useState} from 'react'
function Room() {
const [check, setCheck] = useState(null);
const ibegyouwork = () => {
setCheck(<button>New button</button>);
}
return (
<div>
<button onClick={ibegyouwork}>Display my button now !!!!</button>
{check}
</div>
);
}
export default Room;
When you call setCheck, React basically decides that a rerender is required, and updates the view.

The latter is working because there are no changes to the check value that should appear on the DOM.
If check changes should impact and trigger the React render function, you would want to use a state for show/hide condition.
import React from 'react';
const Check = () => <button>New button</button>;
function Room() {
const [show, setShow] = React.useState(false);
const ibegyouwork = () => {
setShow(true);
}
return (
<div>
<button onClick={ibegyouwork}>Display my button now !!!!</button>
{show && <Check />}
</div>
);
}
export default Room;

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.

why is my if else statement returns true two times?

so I am new to React and I am trying to learn the basics. I got stuck at a point, where I try to show/hide an element on a page. The problem is that when I click Show details, it works, but Hide details must be clicked 2 times in order to do what its supposed to do.
Can you help me understand this?
import React, { useState } from 'react'
const Playground2 = () => {
let visible = false;
const [details, showDetails] = useState();
const [buttonText, changeButtonText] = useState("Show details");
const toggleVisibility = () => {
if (visible) {
showDetails("");
visible = false;
console.log(visible);
changeButtonText("Show details")
} else {
showDetails("Here are some details");
visible = true;
console.log(visible);
changeButtonText("Hide details");
}
}
return (
<>
<section>
<div className="container">
<h1>Visibility Toggle</h1>
<button onClick={toggleVisibility}>{buttonText}</button>
<p>{details}</p>
</div>
</section>
</>
)
}
export default Playground2
You should use the state in your condition. If you declare a variable like your visible one, this will be assigned on every render (every time you set the state with changeButtonText or showDetails. So every time will be set to false. You can simplify your component by doing:
import React, { useState } from 'react'
const Playground2 = () => {
const [visible, setVisible] = useState();
const toggleVisibility = () => {
setVisible(prevState => !prevState)
}
const buttonText = visible ? 'Hide details' : 'Show details'
const details = 'Here are some details'
return (
<>
<section>
<div className="container">
<h1>Visibility Toggle</h1>
<button onClick={toggleVisibility}>{buttonText}</button>
{visible && <p>{details}</p>}
</div>
</section>
</>
)
}
export default Playground2
Well it'd solve your problem if you turn visibility to state as well.
What I think happening is that when you click on button, the visibility variable is turned to false but component isn't refreshed. In order for component to get refreshed, there must be some change in state.
Maybe try that. That should do the trick.
Tip: Variables like loading, visibility, modal closed/open should be state variables.
Move let visible = false out of the component body and this will work as expected, since you are putting visible inside Component, every time the component updates false will be stored in visible.
let visible = false
const Playground 2 = () => {}

How do I add the ability to edit text within a react component?

So here's the user function I'm trying to create:
1.) User double clicks on text
2.) Text turns into input field where user can edit text
3.) User hits enter, and upon submission, text is updated to be edited text.
Basically, it's just an edit function where the user can change certain blocks of text.
So here's my problem - I can turn the text into an input field upon a double click, but how do I get the edited text submitted and rendered?
My parent component, App.js, stores the function to update the App state (updateHandler). The updated information needs to be passed from the Tasks.jsx component, which is where the text input is being handled. I should also point out that some props are being sent to Tasks via TaskList. Code as follows:
App.js
import React, {useState} from 'react';
import Header from './Header'
import Card from './Card'
import cardData from './cardData'
import Dates from './Dates'
import Tasks from './Tasks'
import Footer from './Footer'
import TaskList from './TaskList'
const jobItems= [
{
id:8,
chore: 'wash dishes'
},
{
id:9,
chore: 'do laundry'
},
{
id:10,
chore: 'clean bathroom'
}
]
function App() {
const [listOfTasks, setTasks] = useState(jobItems)
const updateHandler = (task) => {
setTasks(listOfTasks.map(item => {
if(item.id === task.id) {
return {
...item,
chore: task.chore
}
} else {
return task
}
}))
}
const cardComponents = cardData.map(card => {
return <Card key = {card.id} name = {card.name}/>
})
return (
<div>
<Header/>
<Dates/>
<div className = 'card-container'>
{cardComponents}
</div>
<TaskList jobItems = {listOfTasks} setTasks = {setTasks} updateHandler = {updateHandler}/>
<div>
<Footer/>
</div>
</div>
)
}
export default App;
Tasks.jsx
import React, {useState} from 'react'
function Tasks (props) {
const [isEditing, setIsEditing] = useState(false)
return(
<div className = 'tasks-container'>
{
isEditing ?
<form>
<input type = 'text' defaultValue = {props.item.chore}/>
</form>
: <h1 onDoubleClick ={()=> setIsEditing(true)}>{props.item.chore}</h1>
}
</div>
)
}
export default Tasks
TaskList.jsx
import React from 'react'
import Tasks from './Tasks'
function TaskList (props) {
const settingTasks = props.setTasks //might need 'this'
return (
<div>
{
props.jobItems.map(item => {
return <Tasks key = {item.id} item = {item} setTasks = {settingTasks} jobItems ={props.jobItems} updateHandler = {props.updateHandler}/>
})
}
</div>
)
}
export default TaskList
You forgot onChange handler on input element to set item's chore value.
Tasks.jsx must be like below
import React, {useState} from 'react'
function Tasks (props) {
const [isEditing, setIsEditing] = useState(false)
const handleInputChange = (e)=>{
// console.log( e.target.value );
// your awesome stuffs goes here
}
return(
<div className = 'tasks-container'>
{
isEditing ?
<form>
<input type = 'text' onChange={handleInputChange} defaultValue = {props.item.chore}/>
</form>
: <h1 onDoubleClick ={()=> setIsEditing(true)}>{props.item.chore}</h1>
}
</div>
)
}
export default Tasks
So, first of all, I would encourage you not to switch between input fields and divs but rather to use a contenteditable div. Then you just use the onInput attribute to call a setState function, like this:
function Tasks ({item}) {
return(
<div className = 'tasks-container'>
<div contenteditable="true" onInput={e => editTask(item.id, e.currentTarget.textContent)} >
{item.chore}
</div>
</div>
)
}
Then, in the parent component, you can define editTask to be a function that find an item by its id and replaces it with the new content (in a copy of the original tasks array, not the original array itself.
Additionally, you should avoid renaming the variable between components. (listOfTasks -> jobItems). This adds needless overhead, and you'll inevitably get confused at some point which variable is connected to which. Instead say, <MyComponent jobItems={jobItems} > or if you want to allow for greater abstraction <MyComponent items={jobItems} > and then you can reuse the component for listable items other than jobs.
See sandbox for working example:
https://codesandbox.io/s/practical-lewin-sxoys?file=/src/App.js
Your Task component needs a keyPress handler to set isEditing to false when enter is pressed:
const handleKeyPress = (e) => {
if (e.key === "Enter") {
setIsEditing(false);
}
};
Your updateHandler should also be passed to the input's onChange attribute, and instead of defaultValue, use value. It also needs to be reconfigured to take in the onChange event, and you can map tasks with an index to find them in state:
const updateHandler = (e, index) => {
const value = e.target.value;
setTasks(state => [
...state.slice(0, index),
{ ...state[index], chore: value },
...state.slice(index + 1)
]);
};
Finally, TaskList seems like an unnecessary middleman since all the functionality is between App and Task; you can just render the tasks directly into a div with a className of your choosing.
react-edit-text is a package I created which does exactly what you described.
It provides a lightweight editable text component in React.
A live demo is also available.

Trigger usePosition() custom hook onClick element

I'm facing some troubles when I try to trigger usePosition hook in a onClick event.
What I want to achieve is to delay the geolocation permission prompt triggered by the browser until the user clicks some element.
What I've tried so far is a bunch of variations of the following code, but without success:
const IndexPage = () => {
const [geolocation, setGeolocation] = useState({});
const [isGeolocationActive, triggerGeolocation] = useState(false);
let currentPosition = usePosition();
function handleGeolocation() {
if (isGeolocationActive) {
setGeolocation(currentPosition)
console.log('geolocation', geolocation)
} else triggerGeolocation(true);
}
return (
<Layout>
<Wrapper type={`list`} classNames={`wrapper pet__cards cards__list`}>
<Card>
<Image/>
<div onClick={() => handleGeolocation()}>Click me to share my location</div>
I've tried to set a useState hook (initially set to false) which should control and handle the usePosition hook if is set to true but the browser still asking for geolocation permission as soon as I land on the page.
Edit: I've also tried to put the usePosition hook in another component and call it onClick event. However, in this case, I face some hooks rules error such as:
"Invalid hook call. Hooks can only be called inside of the body of a
function component. This could happen for one of the following
reasons..."
Finally I've solved the issue using usePosition hook in another component, something like this:
import React from "react";
import {usePosition} from "use-position";
const Geolocation = () => {
let currentPosition = usePosition();
return (
<div>
<p>{currentPosition.latitude}</p>
</div>
)
};
export default Geolocation;
While in my main component I've used a useState hook which controls the rendering like this:
const IndexPage = () => {
const [geolocation, setGeolocation] = useState('');
function handleGeolocation() {
setGeolocation(<Geolocation/>);
}
return (
<Layout>
<Card>
<Image/>
<div onClick={() => handleGeolocation()}>Click me to share my location</div>
{geolocation}
...

Categories

Resources