React: send data from Child component to Parent component - reactjs

This is a react beginners exercise so I'm looking for the simplest solution. I'm currently learning React so any helpful comments would also be appreciated. Thanks in advance!
This is the exercise:
In a functional component, create a div which contains 2 inputs and one button.
Each of these should be a separate child component, all rendered by a parent component called App.
You should collect the data from the inputs and make it available in the parent component (using a function would work for this).
Input 1 should collect the user's email.
Input 2 should collect the user's password.
When the button (also a separate component) is clicked then you should alert the data collected by the two inputs.
You should use the onChange and onClick events
I'm not quite sure what I have to do with the Button component. This is what I have so far for the exercise but I think it's wrong?......
APP.JS
import React from 'react'
import Email from './components/Email'
import Password from './components/Password'
const App = () => {
getData = data => '${data}'
return ( <div>
<Email getData = {getData} />
<Password getData = {getData} />
</div>
)
}
export default App
EMAIL.JS
import React from 'react'
const App = () => {
let email = '';
return (
<button onClick ={()=>props.getData({email})}></button>
);
}
export default Email;
PASSWORD.JS
import React from 'react'
const App = () => {
let password = '';
return (
<button onClick ={()=>props.getData({password})}></button>
);
}
export default Password;
BUTTON.JS

I have created a solution and a sandbox for the same.
App.js
import React, { useState } from "react";
import Email from "./components/Email";
import Password from "./components/Password";
import Button from "./components/Button";
const App = () => {
const [email, setEmail] = useState("");
const [password, setpassword] = useState("");
const [submit, setSubmit] = useState(false);
if (submit) {
console.log("email ", email);
console.log("password ", password);
}
return (
<div>
<Email setEmail={setEmail} />
<Password setpassword={setpassword} />
<Button setSubmit={setSubmit} />
</div>
);
};
export default App;
Password.js
import React from "react";
const Password = ({ setpassword, submit }) => {
return (
<>
Get Password
<input type="password" onChange={e => setpassword(e.target.value)} />
</>
);
};
export default Password;
Email.js
import React from "react";
const Email = ({ setEmail }) => {
return (
<>
Get Email
<input type="text" onChange={e => setEmail(e.target.value)} />
</>
);
};
export default Email;
Button.js
import React from "react";
const Button = ({ setSubmit }) => {
return (
<button
onClick={() => {
setSubmit(true);
}}
>
Submit
</button>
);
};
export default Button;
I would strongly suggest you go to reactjs.org and go through the documents before starting any course. Please don't copy paste. I hope it helps.

Related

React Button Component doesn't render the label inside the button

I'm creating a simple React App and I've stumbled upon something I can't solve.
I've created a button component which I've exported like any other component.
At the moment, I've imported the Button component in my main part because I need two buttons
The problem is that the labels won't render so i have 2 plain buttons..
The label the button should show is Search
Any fixes?
The Button component
import React from 'react';
import './Button.css';
const Button = ({state = "active"}) => {
return (
<button className={`.btn--${state}`}></button>
);
};
export default Button;
My Main component
import React from 'react';
import './Input.css';
import { useState } from 'react';
import Button from '../Button/Button';
const Input = () => {
const [value, setValue] = useState("");
const SearchButton = (e) => {
e.preventDefault();
console.log("click");
};
const ResetButton = (e) => {
e.preventDefault();
setValue("");
};
return (
<main>
<form className='inputfield'>
<h2 className='input-text'>Zoek een Github user</h2>
<div className='input'>
<input className='search' type='text' placeholder='Typ hier een gebruikersnaam...' value={value} onChange={(e) => setValue(e.target.value)}></input>
<div className='button-field'>
<Button state="inactive" className='search-now' onClick={SearchButton}>Search</Button>
<Button className='reset' onClick={ResetButton}></Button>
</div>
</div>
</form>
</main>
);
};
export default Input;
You have two straight forward ways of this doing what you want.
The first solution would be to use children React Docs Here
Your button then would look like:
const Button = ({state = "active"}) => {
const {children} = props
return (
<button className={`.btn--${state}`}>{children}</button>
);
};
A second approach is to pass the Value through props to the component.
<Button
state="inactive"
className='search-now'
onClick={SearchButton}
textValue={"Search"} />
// Button
const Button = ({state = "active"}) => {
const {textValue} = props
return (
<button className={`.btn--${state}`}>{textValue}</button>
);
};

React Make re-usable input field for URL to replace non http to http

I am trying to write a reusable component for the input field for URL
import React from 'react';
const InputURL = ({ nam, ...rest}) => {
return (
<>
<input
name={name}
{...rest}
/>
</>
);
}
export default InputURL;
I want when user input facebook.com then in onChange it should return with http like it will be https//facebook.com
I will use only this <InputURL onChange={(e) => console.log(e)} />
Can anyone please tell me how can i override onChange to replace non http to http?
Note: I want to write the condition once in the InputURL component only, dont want to write everywhere where i will use it
onChange event may trigger many time so it would be better if you make your logic to add "http" on blur event like this.
import React from 'react';
const InputURL = ({ name, callback, ...rest }) => {
const [value, setValue] = React.useState('');
return (
<>
<input
onBlur={() => callback(`http://${value}`)}
name={name}
onChange={(e) => setValue(e.target.value)}
{...rest}
/>
</>
);
};
export default InputURL;
and in the component where you are using this component find parameter coming in callback.
for example I am logging here.
import React from 'react';
import './style.css';
import InputURL from './InputURL';
export default function App() {
const callback = (val) => {
console.log('>>>>>', val);
};
return (
<div>
<InputURL callback={callback} name="My Input" />
</div>
);
}
See SS hope you are looking the same.
import React from 'react';
const InputURL = ({ nam, prefix, ...rest}) => {
return (
<>
<input
name={nam}
id={"input-"+nam}
onChange={(e) => {
const value = e.target.value;
const input = document.querySelector('#input-'+nam);
if(value.substring(0,prefix.length) != prefix)
{
input.value = prefix;
}
}}
{...rest}
/>
</>
);
}
export default InputURL;
This should work, check it out the reference of Substring here.

TypeError: Cannot read property 'userName' of undefined in react

getting an error like "username undefined" don't know whats going on wrong coz i have declared username stiil getting error plz try to solve my error
https://ibb.co/7jGyCD1
This is my code where i got error
import React from 'react';
import './Message.css';
import {Card,Typography,CardContent} from '#material-ui/core';
const Message = ({message,userName}) => {
const isUser = userName === message.userName;
return (
<div className={`message ${isUser && 'message_user'}`}>
<Card className={isUser ? 'message_userCard' : 'message_guestCard'}>
{/* if the user is logged in then show the user card otherwise show the styiling */}
<CardContent>
<Typography
color='white'
varient='h5'
component='h2'>
<h2>{message.userName} : {message.message}</h2>
</Typography>
</CardContent>
</Card>
</div>
)
}
export default Message;
App.js
This is my app.js file where i wrote my all logic code and i used firebase for store our data in database backend
import React, { useEffect, useState } from 'react';
import './App.css';
import { Button, Input } from '#material-ui/core';
import FormControl from '#material-ui/core/FormControl';
import Message from './Message';
import db from './Firebase';
const App = () => {
const [input, setInput] = useState('');
const [messages, setMessages] = useState([]);
const [userName,setuserName] = useState('');
// console.log(input);
// console.log(messages);
// console.log(userName);
//every single time when database change or add value in dayabase then onSnapshot run
useEffect(()=>{
db.collection('messages').onSnapshot(snapshot=>{
setMessages(snapshot.docs.map(doc=>{
doc.data()
}))
})
},[]);
useEffect(()=>{
setuserName(prompt('enter your name'))
},[])
//in above whenever my input value change inside useEffect code run
const sendMessage = (e) => {
e.preventDefault()
setMessages([...messages, {userName: userName, text:input}])
setInput('');
}
//usestate= variable in react its just a peace of code
//useffetc = it is block of code that can executed based on a condition
return (
<div className="App">
<form>
<h1>Welcome Ashish</h1>
<h2>Welcome {userName}</h2>
<FormControl>
<Input value={input} onChange={(e) => setInput(e.target.value)} />
<Button disabled={!input} variant='contained' color='primary' type='submit' onClick={sendMessage}>send message</Button>
</FormControl>
</form>
{
messages.map(message => (
<Message userName={userName} message={message}/>
))
}
</div>
);
}
export default App;
The first thing you need to ensure that is you need to map over the messages array only if it is present.Consider the code below. I have added a check to render the Message component only if messages are present.
import React, { useEffect, useState } from 'react';
import './App.css';
import { Button, Input } from '#material-ui/core';
import FormControl from '#material-ui/core/FormControl';
import Message from './Message';
import db from './Firebase';
const App = () => {
const [input, setInput] = useState('');
const [messages, setMessages] = useState([]);
const [userName,setuserName] = useState('');
// console.log(input);
// console.log(messages);
// console.log(userName);
//every single time when database change or add value in dayabase then onSnapshot run
useEffect(()=>{
db.collection('messages').onSnapshot(snapshot=>{
setMessages(snapshot.docs.map(doc=>{
doc.data()
}))
})
},[]);
useEffect(()=>{
setuserName(prompt('enter your name'))
},[])
//in above whenever my input value change inside useEffect code run
const sendMessage = (e) => {
e.preventDefault()
setMessages([...messages, {userName: userName, text:input}])
setInput('');
}
//usestate= variable in react its just a peace of code
//useffetc = it is block of code that can executed based on a condition
return (
<div className="App">
<form>
<h1>Welcome Ashish</h1>
<h2>Welcome {userName}</h2>
<FormControl>
<Input value={input} onChange={(e) => setInput(e.target.value)} />
<Button disabled={!input} variant='contained' color='primary' type='submit' onClick={sendMessage}>send message</Button>
</FormControl>
</form>
{
messages && messages.length && messages.map(message => (
<Message userName={userName} message={message}/>
))
}
</div>
);
}
export default App;
useEffect(()=>{
db.collection('messages').onSnapshot(snapshot=>{
setMessages(snapshot.docs.map(doc=>{
// 👇 you miss out the `return` here
return doc.data()
}))
})
},[]);
I think this could be the reason
Maybe when you are creating the use state for username.
Make sure its like this "setUserName". i dont know if its correct or wrong. I always write like this. Change Your Code According to it in your App.js.

Result prints twice in react when using dispatch

This is more of a curiosity question and I feel that it would be useful to know why, but can anyone explain why console.log(recipe) prints twice. When I click Search button the results prints twice in the console. I think i has to do with react re-rendering the component twice, can this be explained in detail.
function Search(props) {
const recipe = useSelector(state => state.recipe)
const dispatch = useDispatch()
const [query, setQuery] = useState("")
console.log(recipe)
const handleQuery = (event) => {
event.preventDefault();
console.log(`Query: ${query}`)
dispatch(fetchRequest(query))
}
return (
<form className={classes.Search} onSubmit={handleQuery}>
<input
className={classes.Search__field}
placeholder="Search over 1,000,000 recipes..."
value={query}
onChange={(e) => setQuery(e.target.value)}
/>
<button className={[buttonClasses.Btn, "search__btn"].join(' ')} type="submit"
>
<svg className={"search__icon"}>
<use href={magnifyingGlass + "#icon-magnifying-glass"}></use>
</svg>
<span>Search</span>
</button>
</form>
);
}
export default Search;
Here is where the Search Component is being used
import React from 'react';
import Search from './Search/Search';
import classes from './Header.module.css';
import logo from '../../img/logo.png';
import Likes from '../Header/Likes/Likes';
const header = (props) => {
return (
<header className={classes.Header}>
<img src={logo} alt="Logo" className={classes.Header__logo} />
<Search />
<Likes />
</header>
)
}
export default header;
Header function is then being used in the Layout function which is
in App.js
import React, { Component } from 'react';
import Aux from '../../hoc/Aux';
import classes from './Layout.module.css';
import Header from '../Header/Header';
import Results from '../Results/Results';
class Layout extends Component {
render() {
return (
<Aux>
<Header />
<Results />
</Aux>
);
}
}
export default Layout;
Here is the redux action
import axios from 'axios';
const FETCH_REQUEST = 'FETCH_USERS_REQUEST'
export const fetchRecipe = (recipe) => {
return {
type: FETCH_REQUEST,
payload: recipe
}
}
export const fetchRequest = (query) => {
console.log(query)
return (dispatch) => {
axios(`https://forkify-api.herokuapp.com/api/search?q=${query}`)
.then(response => {
// console.log(response.data.recipes)
const recipe = response.data.recipes;
dispatch(fetchRecipe(recipe));
})
.catch(error => {
console.log(error)
})
}
}
Ciao, I'm not the maximum expert of react, but you could do a test. You know that useEffect hook is triggered every time component is re-rendered. So you could put your console.log in useEffect and see if will be logged twice. Something like:
useEffect(() => {
console.log(recipe);
})
If you got 2 logs, then it means that Search component is rendered twice. Otherwise could be something related to reactjs workflow and, as I said, I'm not so expert to explain why is logged twice.

react-hook-form - Cannot pass form data to a parent component

I'm using react-hook-form and trying to pass the data from a form to its parent.
For this I'm trying to pass the data via a callback, but it's not working.
When I console.log data inside the parent component, I get undefined.
Parent component
import React from 'react';
import InputForm from './InputForm';
const App = (data) => {
const onFormSubmit = () => {
console.log(data.name);
};
return (
<div className='App'>
<InputForm onceSubmited={() => onFormSubmit()} />
</div>
);
};
export default App;
Child component
import React from 'react';
import { useForm } from 'react-hook-form';
const InputForm = ({ onceSubmited }) => {
const { register, handleSubmit } = useForm();
const onSubmit = (data) => {
console.log(data);
onceSubmited(data);
};
return (
<>
<form onSubmit={handleSubmit(onSubmit)}>
<input
type='text'
name='name'
ref={register}
/>
<input
type='email'
name='email'
ref={register}
/>
<button type='submit'>
Submit
</button>
</Form>
</>
);
};
export default InputForm;
You need to pass the argument in your arrow function. This should make it work:
import React from 'react';
import InputForm from './InputForm';
const App = () => {
const onFormSubmit = (data) => {
console.log(data.name);
};
return (
<div className='App'>
<InputForm onceSubmited={(data) => onFormSubmit(data)} />
</div>
);
};
export default App;

Resources