How to send values React Hook(props) - javascript

I made My code.
When Click the button A, appear AAA.
Or Click the button B, appear BBB, Click the button C, appear CCC.
// Main > RightMain.js
import React, { useState } from 'react'
function RightMain() {
const [screen, setScreen] = useState('');
const A = () => {
setScreen('A')
}
const B = () => {
setScreen('B')
}
const C = () => {
setScreen('C')
}
return (
<div>
<button onClick={A}>A</button>
<button onClick={B}>B</button>
<button onClick={C}>C</button>
{screen === 'A' && <div>AAA</div>}
{screen === 'B' && <div>BBB</div>}
{screen === 'C' && <div>CCC</div>}
</div>
)
}
export default RightMain
And I wanna separate My Code(RightMain.js).
When I Click the Button on the RightMain.js.
The Result appear's on the Formations.js like the image below.
But I don kno how to bring value(RightMain.js's screen) to the Formations.js.
// Main > LeftMain.js
import React from 'react'
import RadioBtn from './LeftMain/RadioBtn';
import Formation from './LeftMain/Formation';
function LeftMain() {
return (
<div>
<div>
<RadioBtn />
</div>
<div>
<Formation />
</div>
</div>
)
}
export default LeftMain
//Main > LeftMain > Formation.js
import React, { useState } from 'react'
import RightMain from '../RightMain';
function Formation() {
return (
<div>
</div>
)
}
export default Formation
Thx

If I understand correctly, LeftMain and RightMain are sibilings, and Formation is a child of LeftMain.
One possible approach is to use Context API.
Something like this should work:
// Define the default value
// or return null and take that into consideration when using "useContext"
export const MyCurrentScreenContext = React.createContext({
setScreen: () => void 0,
screen: ''
});
export const MyCurrentScreenProvider = props => {
const [screen, setScreen] = useState('');
const value = useMemo(() => ({ screen, setScreen }), [screen, setScreen]);
return (
<MyCurrentScreenContext.Provider value={value}>
{props.children}
</MyCurrentScreenContext.Provider>
);
}
const Main = () => {
...
return (
<MyCurrentScreenProvider>
<LeftMain />
<RightMain />
...
</MyCurrentScreenProvider>
);
}
const RightMain() {
const { setScreen } = useContext(MyCurrentScreenContext);
....
};
const Formation() {
const { screen } = useContext(MyCurrentScreenContext);
....
};
Read more about context api at the official docs

From what I understand, you want to pass the values down to the child components. If that is correct then you could pass them as parameters when calling it and using props to receive them inside the child component. Something like this.
<div>
<RadioBtn randomVal="value" />
</div>

Related

Rendering two React components in a sequence

In Summary: {
How can I merge those 2 arrays into one. As in, instead of having it like this:
[1st,new1st,...] [2nd,new2nd,...]
I want it to be like this:
[1st,2nd,new1st,new2nd,...]
}
I have this note app that I am creating. I am trying to render the two components so that each note rendered is kind of the last element of an array. So, in short, I want each component to be below the previous added note (think of it like a list where each added input is added after the previous list items).
So, this is how it looks before adding anything.
and this is how it looks after adding one note on each create area.
and this is what I am trying to avoid after adding the new notes from each note create area.
What I want is
-1st -2nd -new1st - new2nd
As in no matter which create area I use, it gets rendered after all the previous ones.
Here's my code
import React, { useState } from "react";
import Header from "./Header";
import Footer from "./Footer";
import Note from "./Note";
import CreateArea from "./CreateArea";
function App() {
const [notes, setNotes] = useState([]);
const [notes2, setNotes2] = useState([]);
function addNote(newNote) {
setNotes(prevNotes => {
return [...prevNotes, newNote];
});
}
function addNote2(newNote) {
setNotes2(prevNotes => {
return [...prevNotes, newNote];
});
}
function deleteNote(id) {
setNotes(prevNotes => {
return prevNotes.filter((noteItem, index) => {
return index !== id;
});
});
}
function deleteNote2(id) {
setNotes(prevNotes => {
return prevNotes.filter((noteItem, index) => {
return index !== id;
});
});
}
return (
<div>
<Header />
<CreateArea onAdd={addNote} />
<CreateArea onAdd={addNote2} />
{notes.map((noteItem, index1) => {
return (
<Note
key={index1}
id={index1}
title={noteItem.title}
content={noteItem.content}
onDelete={deleteNote}
/>
);
})}
{notes2.map((noteItem, index2) => {
return (
<Note
key={index2}
id={index2}
title={noteItem.title}
content={noteItem.content}
onDelete={deleteNote2}
/>
);
})}
<Footer />
</div>
);
}
export default App;
You can test the app by copying the above code instead of App.jsx at CodeSandbox.
I need to do something like that:
-item1
=nested item 1
=nested item 2
=nested item 3
-item 2
so I need the second create area to eventually be used for nested items (children). and the 1st create area to be for 'item1' or 'item2' or ... (parent). But with the way it functions from my code, it gets rendered like that:
-item1
-item2
=nested item 1
=nested item 2
=nested item 3
I don't understand a reason why you would want to do that. You need to either have one list or two. If for rendering, you want it to be one list, you can have that in a single state. Also if it's just about having two input fields to add note, both fields can push to same state. Here is how it could be:
import React, { useState } from "react";
import Header from "./Header";
import Footer from "./Footer";
import Note from "./Note";
import CreateArea from "./CreateArea";
function App() {
const [notes, setNotes] = useState([]);
function addNote(newNote) {
setNotes(prevNotes => {
return [...prevNotes, newNote];
});
}
function deleteNote(id) {
setNotes(prevNotes => {
return prevNotes.filter((noteItem, index) => {
return index !== id;
});
});
}
return (
<div>
<Header />
<CreateArea onAdd={addNote} />
<CreateArea onAdd={addNote} />
{notes.map((noteItem, index1) => {
return (
<Note
key={index1}
id={index1}
title={noteItem.title}
content={noteItem.content}
onDelete={deleteNote}
/>
);
})}
<Footer />
</div>
);
}
export default App;
Well, if you still want it :D then here is a thing you can do:
import React, { useState } from "react";
import Header from "./Header";
import Footer from "./Footer";
import Note from "./Note";
import CreateArea from "./CreateArea";
function App() {
const [notes, setNotes] = useState([]);
const [notes2, setNotes2] = useState([]);
const [combinedNotes, setCombinedNotes] = useState([]);
useEffect(() => {
const notesList = [...notes, ...notes2].sort((note1, note2) => note1.timestamp - note2.timestamp);
setCombinedNotes(notesList);
}, [notes, notes2]);
function addNote(newNote) {
setNotes(prevNotes => {
return [...prevNotes, { ...newNote, timestamp: new Date().getTime() }];
});
}
function addNote2(newNote) {
setNotes2(prevNotes => {
return [...prevNotes, { ...newNote, timestamp: new Date().getTime() }];
});
}
function deleteNote(id) {
const isFirstNote = notes.find((note) => note.timestamp === id);
if (isFirstNote) {
setNotes(prevNotes => {
return prevNotes.filter((noteItem) => {
return noteItem.timestamp !== id;
});
});
} else {
setNotes2(prevNotes => {
return prevNotes.filter((noteItem) => {
return noteItem.timestamp !== id;
});
});
}
}
return (
<div>
<Header />
<CreateArea onAdd={addNote} />
<CreateArea onAdd={addNote2} />
{combinedNotes((noteItem, index) => {
return (
<Note
key={index}
id={noteItem.timestamp}
title={noteItem.title}
content={noteItem.content}
onDelete={deleteNote}
/>
);
})}
<Footer />
</div>
);
}
export default App;

disable another button on react js

I have a React project which has three buttons in it. When I click a button, For example I click the second button, it will log "this is from button two ". followed by a random number with time interval 500ms. My aim is when I click a button, another buttons will be disabled, how to do this? Thanks in advance.
This is my App.js
import './App.css';
import { Button1 } from './Button1.React';
import { Button2 } from './Button2.React';
import { Button3 } from './Button3.React';
function App() {
return (
<div className="App">
<Button1>log one</Button1>
<Button2>log two</Button2>
<Button3>log three</Button3>
</div>
);
}
export default App;
this is my Button1.React.jsx
import React from "react";
export const Button1 = ({
children,
type,
doStuff1 = ()=> {
console.log("this is button one ", Math.random().toFixed(1));
setTimeout(doStuff1, 500);
},
})=>{
return (
<button onClick={doStuff1} type={type}>
{children}
</button>
)
};
this is my Button2.React.jsx
import React from "react";
export const Button2 = ({
children,
type,
doStuff2 = ()=> {
console.log("this is button two ", Math.random().toFixed(1));
setTimeout(doStuff2, 500);
}
})=>{
return (
<button onClick={doStuff2}type={type}>
{children}
</button>
)
};
this is my Button3.React.jsx
import React, { Children } from "react";
export const Button3 = ({
children,
type,
doStuff3 = ()=> {
console.log("this is button three ", Math.random().toFixed(1));
setTimeout(doStuff3, 500);
}
})=>{
return (
<button onClick={doStuff3}type={type}>
{children}
</button>
)
};
and here's the screenshot from the rendered page
i've been told to do this is using useState that returns a boolean condition to disable/enable click, but i don't know how to do this.
function App() {
const [bool1, setBool1] = useState(false);
const [bool2, setBool2] = useState(false);
const onClickBtn1 = () => {
// your other codes when btn 1 is clicked
// ------
setBool2(true); //disable button 2
}
const onClickBtn2 = () => {
// your codes when btn 2 is clicked
// ------
setBool1(true); //disable button 1
}
return (
<div className="App">
<button onClick={onClickBtn1} disabled={bool1}>Button 1</button>
<button onClick={onClickBtn2} disabled={bool2}>Button 2</button>
</div>
);
}
Basically, you use disabled in the button element, the value of the disabled should be in the state so the app will re-render if its value changes. Finally, change the value of the state on the button's onClick handler. You need to learn to use React Hooks for this since you said you don't know useState.
Is this what you try to do
import React, { useState } from "react";
const Button = ({ children, type, disabled, doStuff }) => {
return (
<button onClick={doStuff} type={type} disabled={disabled}>
{children}
</button>
);
};
export default function Name() {
const [activeButton, setActiveButton] = useState();
const disableOtherButtons = (currentButton) => {
if (activeButton === currentButton) {
setActiveButton()
} else {
setActiveButton(currentButton)
}
}
const doStuff1 = () => {
console.log("this is button one ", Math.random().toFixed(1));
disableOtherButtons('button1');
};
const doStuff2 = () => {
console.log("this is button one ", Math.random().toFixed(1));
disableOtherButtons("button2");
};
const doStuff3 = () => {
console.log("this is button one ", Math.random().toFixed(1));
disableOtherButtons("button3");
};
return (
<div>
<Button
doStuff={doStuff1}
disabled={activeButton && activeButton !== "button1"}
>
Button1
</Button>
<Button
doStuff={doStuff2}
disabled={activeButton && activeButton !== "button2"}
>
Button2
</Button>
<Button
doStuff={doStuff3}
disabled={activeButton && activeButton !== "button3"}
>
Button2
</Button>
</div>
);
}
You can pass reference useState to buttons
import { useRef } from 'react';
import './App.css';
import { Button1 } from './Button1.React';
import { Button2 } from './Button2.React';
import { Button3 } from './Button3.React';
function App() {
const [ btn1, setBtn1 ] = useState(false) // by default disabled will be set to false
const [ btn2, setBtn3 ] = useState(false)
const [ btn3, setBtn3 ] = useState(false)
const actions = [ setBtn1, setBtn2, setBtn3 ]
return (
<div className="App">
<Button1 actions={actions} disabled={btn1}>log one</Button1>
<Button2 actions={actions} disabled={btn2}>log two</Button2>
<Button3 actions={actions} disabled={btn3}>log three</Button3>
</div>
);
}
export default App;
and for buttons you need to accept those parameters and work with them
import React from "react";
export const Button1 = (props) => {
const { children, type, actions, disabled } = props
const doStuff = () => {
actions[0](false); // this will set Button1 to disabled
console.log("this is button three ", Math.random().toFixed(1));
setTimeout(doStuff3, 500);
}
return (
<button onClick={doStuff} type={type} disabled={disabled}>
{children}
</button>
)
};
I can see you do setTimeout(doStuff3, 500); inside of function that is referencing undefined function, you might want to define those in App.js and pass those as props to buttons

React toggle view functionality in the parent via child component

I am trying to toggle view between list of meals and meal details. I have placed a button in the child component Meal.js to the Meals.js which is meant to be the list and the details view.
Can you please help me fix this issue. Seems like its not working even with the conditional rendering method I've used in the code below.
Meal.js
import { useState } from 'react'
import './Meal.css'
const Meal = (props) => {
const [isToggled, setIsToggled] = useState(false);
const sendIdHandler = () => {
if (isToggled === true) {
setIsToggled(false);
}
else {
setIsToggled(true);
}
props.onSaveIdHandler(props.id, isToggled)
}
return (
<div
className='meal'
onClick={sendIdHandler}
>
{props.label}
</div>
);
}
export default Meal;
Meals.js
import Meal from './Meal/Meal'
const Meals = (props) => {
let toggleCondition = false;
const saveIdHandler = (data, isToggled) => {
toggleCondition = isToggled;
const mealDetails = props.mealsMenuData.findIndex(i =>
i.id === data
)
console.log(mealDetails, toggleCondition)
}
return (
<div>
{toggleCondition === false &&
props.mealsMenuData.map(item =>
<Meal
key={item.id}
id={item.id}
label={item.label}
onSaveIdHandler={saveIdHandler}
/>
)
}
{toggleCondition === true &&
<div>Horray!</div>
}
</div>
);
}
export default Meals;
UPDATE
Finally figured how to do this properly. I put the condition true/false useState in the parent instead and have Meal.js only send the id I need to view the item
Code is below..
Meals.js
import { useState } from 'react'
import Meal from './Meal/Meal'
import MealDetails from './MealDetails/MealDetails'
const Meals = (props) => {
const [show, setShow] = useState(false);
const [mealId, setMealId] = useState(0);
const saveIdHandler = (data) => {
setShow(true);
setMealId(props.mealsMenuData.findIndex(i =>
i.id === data)
)
console.log(props.mealsMenuData[mealId].ingridients)
}
const backHandler = () => {
setShow(false)
}
return (
<div>
{show === false &&
props.mealsMenuData.map(item =>
<Meal
key={item.id}
id={item.id}
label={item.label}
onSaveIdHandler={saveIdHandler}
/>
)
}
{show === true &&
<div>
<MealDetails data={props.mealsMenuData[mealId]} />
<button onClick={backHandler}>Back</button>
</div>
}
</div>
);
}
export default Meals;
Meal.js
import './Meal.css'
const Meal = (props) => {
const sendIdHandler = () => {
props.onSaveIdHandler(props.id)
}
return (
<div
className='meal'
onClick={sendIdHandler}
>
{props.label}
</div>
);
}
export default Meal;
Your problem in sendIdHandler: You can update like this:
const sendIdHandler = () => {
const newIsToggled = !isToggled;
setIsToggled(newIsToggled)
props.onSaveIdHandler(props.id, newIsToggled)
}

React when I update state on one element all parent element and their parents functions are called, trying to understand React re-rendering?

I've created a very simplified code version of my problem to understand the REACT rendering using typescript. When I click a button which changes state in the lowest child element all parent elements are updated by the renderer and their children on other forks. How can I change the below so it doesn't do that.
import * as React from 'react';
import { connect } from 'react-redux';
import './Grid.css';
const RenderPopup = (key: number) => {
const open = () => setShowDialog(true);
const [showDialog, setShowDialog] = React.useState(false);
const close = () => setShowDialog(false);
if (!showDialog) {
return (
<div>
<button onClick={open}>do it</button>
</div>
)
}
else {
return (
<div>
<button onClick={close}>close
</button>
</div>
)
}
}
function Cell(key:number) {
return (
<div key={key}>
{key}
{RenderPopup(key)}
</div>
)
}
const Header = () => {
return (
<div className="gridRow">
{Cell(0)}
{Cell(1)}
{Cell(2)}
</div>
)
}
const Person = (rowNum: number) => {
return (
<div key={rowNum} className="gridRow">
{Cell(0)}
{Cell(1)}
{Cell(2)}
</div>
)
}
const Persons = () => {
return (
<div>
{Person(1)}
{Person(2)}
{Person(3)}
</div>
)
}
const Grid = () => {
return (
<div>
<Header />
<Persons />
</div>
);
}
export default connect()(Grid);

Generating Search suggestions in React?

I am looking to generate search suggestions that match data collected, like so:
As you type in you get suggestions:
I am referencing some of the tutorial work from WesBos:
https://github.com/wesbos/JavaScript30/blob/master/06%20-%20Type%20Ahead/index-FINISHED.html
I've got the data logging in the console but now I am unsure how to get it to render. Below are my components (My thoughts were to generate the divs as a loop in App.js and pass the props to Match.js which I would eventually import but I am not sure if I am approaching this wrong):
App.js
import React, { Component } from 'react';
import { Form, Button } from 'react-bootstrap';
import 'bootstrap/dist/css/bootstrap.min.css';
const my_data = require('./data/test.json')
class App extends Component {
constructor(props) {
super(props);
this.state = {
links: [],
selectedLink:null,
userLocation: {},
searchInput: "",
showMatches: false,
matches: []
};
}
componentDidMount() {
fetch('https://data.cityofnewyork.us/resource/s4kf-3yrf.json')
.then(res=> res.json())
.then(res=>
//console.log(json)
this.setState({links:res})
);
}
render() {
const handleInputChange = (event) => {
event.preventDefault()
this.setState({searchInput: event.target.value })
//console.log(event.target.value)
}
const handleSubmit = (event) => {
event.preventDefault()
const data = this.state
displayMatches();
}
const findMatches = (wordToMatch, my_obj) => {
return my_obj.filter(place => {
// here we need to figure out the matches
const regex = new RegExp(wordToMatch, 'gi');
//console.log(place.street_address.match(regex))
return place.street_address.match(regex)
});
}
const displayMatches =() => {
const matchArray = findMatches(this.state.searchInput, this.state.links);
matchArray.map(place => {
console.log(place.street_address);
this.setState({matches:place})
this.setState({showMatches:true})
});
}
return (
<div>
<Form style = {{width: "75%"}} onSubmit = {handleSubmit}>
<Form.Group controlId="formSearch">
<Form.Control
type="text"
name = "my_search"
placeholder="Search for a Link Near you..."
onChange = {handleInputChange} />
</Form.Group>
<Button variant="primary" type="submit">
Search
</Button>
</Form>
<div>
{`How can I generate the console logged values as dynammic suggestions?`}
</div>
</div>
);
}
}
export default App;
Match.js
import React from 'react';
const match = ( props ) => {
return (
<div className="Matches">
<p>{`data is passed: ${props.address}`}</p>
</div>
)
};
export default match;
Appreciate the help.
Answers - Using Suggestions below
App.js
import React, { Component } from 'react';
import { Form, Button, ListGroup } from 'react-bootstrap';
import 'bootstrap/dist/css/bootstrap.min.css';
import Match from './Match'
const my_data = require('./data/test.json')
class App extends Component {
state = {
links: [],
selectedLink:null,
userLocation: {},
searchInput: "",
showMatches: false,
matches: [],
searchLink:[]
}
componentDidMount() {
fetch('https://data.cityofnewyork.us/resource/s4kf-3yrf.json')
.then(res=> res.json())
.then(res=>
//console.log(json)
this.setState({links:res})
);
}
handleInputChange = (event) => {
event.preventDefault()
this.setState({searchInput: event.target.value })
console.log(event.target.value)
}
handleSubmit = (event) => {
event.preventDefault()
this.displayMatches();
}
findMatches = (wordToMatch, my_obj) => {
return my_obj.filter(place => {
// here we need to figure out the matches
const regex = new RegExp(wordToMatch, 'gi');
//console.log(place.street_address.match(regex))
return place.street_address.match(regex)
});
}
displayMatches =() => {
const matchArray = this.findMatches(this.state.searchInput, this.state.links);
const newStateMatches = matchArray.map(place => {
console.log(place.street_address);
return place
});
this.setState({matches:newStateMatches})
this.setState({showMatches:true})
}
alertClicked =(event) => {
//alert('you clicked an item in the group')
const data = event.target
console.log('clicked this data:', data)
this.setState({searchLink: event.target})
console.log(this.state.searchLink)
}
render() {
return (
<div>
<input
placeholder="Search for a Link Near you..."
onChange = {this.handleInputChange}
value = {this.state.searchInput}
/>
<Button onClick={this.handleSubmit}>
Search
</Button>
<ListGroup defaultActiveKey="#link1">
{
this.state.matches.map(match => {
return <Match
address={match.street_address}
alertClicked={this.alertClicked}/>
})
}
</ListGroup>
</div>
);
}
}
export default App;
Match.js
import React from 'react';
import { ListGroup } from 'react-bootstrap';
const match = ( props ) => {
return (
<ListGroup.Item
className="Matches"
action onClick={props.alertClicked}>
<p>{`${props.address}`}</p>
</ListGroup.Item>
)
};
export default match;
I think your initial instinct as to how to do this is correct :
get the matches
store them in state
map over the state and render one component per match, passing the relevant data as props
To answer your question exactly, mapping over state to render component usually looks something like this :
<div>
{
matches.map(match => {
return <Match address={match.address} name={match.name} />
})
}
</div>
You can also destructure properties like this :
<div>
{
matches.map(({address, name}) => {
return <Match address={address} name={name} />
})
}
</div>
Also, another minor observation: you notice I called the component Match with a capital M. It is a convention in React and other component based libraries that components' names are always capitalized, not only in the file name but also in the code.
First move all your method definitions outside of your render function (you'll need to update const and add this.
in your display matches you should be building a newstate array then setState with the new array once built
i do not use react bootstrap but it did not appear that your submit button was within the form therefor was not submitting the form.
Make sure react components are capitalized (match component should be Match)
I passed the whole 'place' down to the Match component via place prop:
<Match place={place} />
if you want to access the address like you did you would need to pass each individual value from the place down to the Match component like:
<Match address={place.address} />
(also if you are only initializing state before first render you can do so outside of the constructor)
I simplified the return statement to just use a plain input and button tag for simplicity but you can probably get going from here
Working Snippet:
const Match = ( props ) => {
return (
<div className="Matches">
<p>{`data is passed: ${props.place.street_address}`}</p>
</div>
)
};
class SomeComponent extends React.Component{
state = {
links: [],
selectedLink:null,
userLocation: {},
searchInput: "",
showMatches: false,
matches: []
}
componentDidMount() {
fetch('https://data.cityofnewyork.us/resource/s4kf-3yrf.json')
.then(res=> res.json())
.then(res=>
//console.log(json)
this.setState({links:res})
);
}
handleInputChange = (event) => {
event.preventDefault()
this.setState({searchInput: event.target.value })
//console.log(event.target.value)
}
handleSubmit = (event) => {
event.preventDefault()
this.displayMatches();
}
findMatches = (wordToMatch, my_obj) => {
return my_obj.filter(place => {
// here we need to figure out the matches
const regex = new RegExp(wordToMatch, 'gi');
//console.log(place.street_address.match(regex))
return place.street_address.match(regex)
});
}
displayMatches =() => {
const matchArray = this.findMatches(this.state.searchInput, this.state.links);
const newStateMatches = matchArray.map(place => {
console.log(place.street_address);
return place
});
this.setState({matches:newStateMatches})
this.setState({showMatches:true})
}
render() {
return (
<div>
<input
placeholder="Search for a Link Near you..."
onChange = {this.handleInputChange}
value = {this.state.searchInput}
/>
<button onClick={this.handleSubmit}>
Search
</button>
{this.state.matches.map((place)=>{
return <Match place={place} />
})}
</div>
);
}
}
ReactDOM.render(
<SomeComponent />,
document.getElementById("react")
);
<div id='react'></div>
<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>

Categories

Resources