React js communication between two components - javascript

I am new to ReactJS and building one simple application using few components like UserForm, UserGrid and App.
UserForm displays simple textbox and button like below.
and when user input some text inside textbox and hit the save button it will display data in UserGrid component as per the screenshot.
but i can't figure it out how edit will work ? like when i hit the edit it will fetch the information from the grid and fill the textbox so i can update the data, can someone please help me on this ? how can i achieve this ?
below is my code
UserForm.js
import React,{createRef} from 'react';
const UserForm = (props) =>{
const username = createRef();
const saveUser = () =>{
debugger
if(username.current.value !== ''){
props.handleSubmit(username.current.value);
username.current.value='';
username.current.focus();
}
}
return(
<div className="row">
<div className="col-md-6">
<div className="form-group">
<input type="text" className="form-control" placeholder="Username" ref={username} />
</div>
</div>
<div className="col-md-12">
<button className="btn btn-primary" onClick={saveUser}>Save</button>
</div>
</div>
)
}
export default UserForm;
UserGrid.js
import React from 'react';
const UserGrid = (props) =>{
debugger
return(
<div className="row">
<div className="col-md-12">
<table className="table">
<tr>
<th>
Username
</th>
<th>
</th>
</tr>
{
props.list.map(item =>
<tr>
<td>
{item}
</td>
<td>
<button>Edit</button>
<button>Delete</button>
</td>
</tr>
)
}
</table>
</div>
</div>
)
}
export default UserGrid;
App.js
import React,{useState} from 'react';
import UserForm from './UserForm';
import UserGrid from './UserGrid';
function App() {
const [list, setList] = useState([]);
const handleSubmit = (username) =>{
setList([...list,username]);
}
return (
<div className="App">
<UserForm handleSubmit={handleSubmit}></UserForm>
<UserGrid list={list}></UserGrid>
</div>
);
}
export default App;

You have to bring your edit state up to App component rather than using ref in the child component. Just like you have handleSubmit in App.js have a handleEdit function and pass that function to UserGrid.
function App() {
const [list, setList] = useState([]);
const [name, setName] = useState('');
const [editIndex, setEditIndex] = useState(null);
const handleSubmit = () => {
if(editIndex) {
setList([...list.slice(0, index), name, ...list.slice(index+1)])
setEditIndex(null)
} else {
setList([...list, name]);
}
setName('')
}
return (
<div className="App">
<UserForm name={name} setName={setName} handleSubmit={handleSubmit}></UserForm>
<UserGrid setEditIndex={setEditIndex} list={list}></UserGrid>
</div>
);
}
UserForm.js
import React,{createRef} from 'react';
const UserForm = ({name, setName}) =>{
const saveUser = () =>{
debugger
if(name !== ''){
props.handleSubmit();
}
}
return(
<div className="row">
<div className="col-md-6">
<div className="form-group">
<input type="text" className="form-control" placeholder="Username" value={name} onChange={(e) => setName(e.target.value)} />
</div>
</div>
<div className="col-md-12">
<button className="btn btn-primary" onClick={saveUser}>Save</button>
</div>
</div>
)
}
export default UserForm;
To support edit:
{
props.list.map((item, index) =>
<tr>
<td>
{item}
</td>
<td>
<button onClick={setEditIndex(index)}>Edit</button>
<button>Delete</button>
</td>
</tr>
)
}

This is working example while i am using your code.
Nothing special in App.js. You can take a look a logic.
App.js
import React, { useState } from "react";
import UserForm from "./UserForm";
import UserGrid from "./UserGrid";
function App() {
const [list, setList] = useState([]);
const handleSubmit = username => {
setList([...list, username]);
};
const editList = item => {
setList(
list.map(l => ({
...l,
username: l.id === item.id && item.newValue ? item.newValue : l.username
}))
);
};
const deleteList = id => {
setList(list.filter(l => l.id !== id));
};
return (
<div className="App">
<UserForm handleSubmit={handleSubmit}></UserForm>
<UserGrid
list={list}
editList={editList}
deleteList={deleteList}
></UserGrid>
</div>
);
}
export default App;
This is UserForm Component. I think you will have problems, if you dont have someting like ID, cuz if you have lists with same names, your delete and edit function will work wrong.
UseForm.js
import React, { createRef } from "react";
const UserForm = ({ handleSubmit }) => {
const username = createRef();
const saveUser = () => {
if (username.current.value !== "") {
const randomId = () => "_" + Math.random().toString(36).substr(2, 9);
handleSubmit({ username: username.current.value, id: randomId() });
username.current.value = "";
username.current.focus();
}
};
return (
<div className="row">
<div className="col-md-6">
<div className="form-group">
<input
type="text"
className="form-control"
placeholder="Username"
ref={username}
/>
</div>
</div>
<div className="col-md-12">
<button className="btn btn-primary" onClick={saveUser}>
Save
</button>
</div>
</div>
);
};
export default UserForm;
And this is UserGrid component. I hope you will undarstand the code. If you have questions, go ahead.
UseGrid.js
import React, { useState } from "react";
const UserGrid = ({ list, editList, deleteList }) => {
const [isEditable, setIsEditable] = useState(false);
const [value, setValue] = useState("");
return (
<div className="row">
<div className="col-md-12">
<table className="table">
<tr>
<th>Username</th>
<th></th>
</tr>
{list.map(item => (
<tr>
<td>{item.username}</td>
{isEditable ? (
<>
<td>
<input
type="text"
className="form-control"
placeholder="Username"
onChange={e => setValue(e.target.value)}
/>
</td>
<button onClick={() => setIsEditable(false)}>CANCEL</button>
</>
) : null}
<td>
<button
onClick={() => {
editList({
id: item.id,
username: item.username,
newValue: value
});
setIsEditable(true);
}}
>
Edit
</button>
<button onClick={() => deleteList(item.id)}>Delete</button>
</td>
</tr>
))}
</table>
</div>
</div>
);
};
export default UserGrid;

Related

Render component after change in another component

In my app I am adding new product. But after I click second time it appears on a list. In AddProducts I console.log the array with changed list and it had been updated. But in mu ProductList it doesnt work after click Add so I apply componentDidUpdate to react on changes. Also I use useEffect in App to react on changes sending from AddProduct but it works only I click second times.
AddProduct
function AddProducts(props) {
const [newProduct, setNewProduct] = useState({
productName: ``,
category: ``,
groceries: false,
});
const [newList, setNewList] = useState(props.productsToDisplay);
function handleChange(event) {
event.preventDefault();
setNewProduct({
...newProduct,
[event.target.name]:
event.target.type === "checkbox"
? event.target.checked
: event.target.value,
});
}
function handleAddNewProduct() {
const addProduct = newList.concat([
{
nazwa: newProduct.productName,
kategoria: newProduct.category,
produktSpozywczy: newProduct.groceries,
},
]);
setNewList(addProduct);
props.sendNewProductsToParent(newList);
}
return (
<div className={styles.Wrapper}>
{console.log(newList)}
<p>Add products</p>
<input
name="productName"
value={newProduct.productName}
onChange={handleChange}
></input>
<input
name="category"
value={newProduct.category}
onChange={handleChange}
></input>
<input
name="groceries"
type="checkbox"
value={newProduct.groceries}
onChange={handleChange}
></input>
<p>Is it groceries?</p>
<button onClick={handleAddNewProduct}>Add new product</button>
</div>
);
}
ProductList
class ProductsList extends React.Component {
constructor(props) {
super(props);
}
render() {
return (
<div className={commonColumnsStyles.App}>
{console.log(this.props.productsToDisplay)}
<header className={commonColumnsStyles.AppHeader}>
<p>Products list</p>
<ul>
{this.props.productsToDisplay.map((currProduct, index) => (
<li key={index} onClick={() => this.addProduct(index)}>
{currProduct.nazwa}
</li>
))}
</ul>
</header>
</div>
);
}
}
function App() {
const [resultToDisplay, setResultToDisplay] = useState(``);
const [newProductsList, setNewProductsList] = useState(produkty);
return (
<div className={styles.appWrapper}>
{console.log(newProductsList)}
<AddProducts
productsToDisplay={produkty}
sendNewProductsToParent={setNewProductsList}
/>
<div className={styles.columnsWrapper}>
<ProductsList
productsToDisplay={newProductsList}
sendAddedProductsToParent={setResultToDisplay}
/>
</div>
</div>
);
}
Solution for community:
AddProduct
function AddProducts(props) {
const [newProduct, setNewProduct] = useState({
productName: ``,
category: ``,
groceries: false,
});
function handleChange(event) {
event.preventDefault();
setNewProduct({
...newProduct,
[event.target.name]:
event.target.type === "checkbox"
? event.target.checked
: event.target.value,
});
}
function handleAddNewProduct() {
const addProduct = {
nazwa: newProduct.productName,
kategoria: newProduct.category,
produktSpozywczy: newProduct.groceries,
};
props.sendNewProductToParent((listOfPrimiaryProducts) => [
...listOfPrimiaryProducts,
addProduct,
]);
}
return (
<div className={styles.Wrapper}>
<p>Add products</p>
<input
name="productName"
value={newProduct.productName}
onChange={handleChange}
></input>
<input
name="category"
value={newProduct.category}
onChange={handleChange}
></input>
<input
name="groceries"
type="checkbox"
value={newProduct.groceries}
onChange={handleChange}
></input>
<p>Is it groceries?</p>
<button onClick={handleAddNewProduct}>Add new product</button>
</div>
);
}
export default AddProducts;
ProductList
class ProductsList extends React.Component {
constructor(props) {
super(props);
}
render() {
return (
<div className={commonColumnsStyles.App}>
<header className={commonColumnsStyles.AppHeader}>
<p>Products list</p>
<ul>
{this.props.productsInShop.map((currProduct, index) => (
<li key={index} onClick={() => this.addProduct(index)}>
{currProduct.nazwa}
</li>
))}
</ul>
</header>
</div>
);
}
}
export default ProductsList;
App
function App() {
const [productsList, setProductsList] = useState(produkty);
return (
<div className={styles.appWrapper}>
<AddProducts sendNewProductToParent={setProductsList} />
<div className={styles.columnsWrapper}>
<ProductsList
productsInShop={productsList}
/>
</div>
</div>
);
}
export default App;

Parsing error: 'return' outside of function

I've recently started maintaining JavaScript code and now i am facing trouble in the returning function in line 39. Can someone please help? I have checked the syntax and can't find anything wrong................................................................???????????????????????????????????????????????????????????????????????
import React, { useState, useEffect} from "react";
import axios from "axios";
import { renderMatches, useNavigate, useParams, } from "react-router-dom";
const Editorg = () => {
let navigate = useNavigate();
const {id} = useParams();
console.log(id);
const [user, setUser] = useState ({
name:"",
poname:"",
type:"",
ostatus:"",
});
const { name, poname, type, ostatus } = user;
const onInputChange = e => {
setUser({...user, [e.target.name]: e.target.value });
};
useEffect(() => {
loadUser();
}, []);
};
const onSubmit = async e => {
e.preventDefault();
await axios.put( 'http://localhost:3001/users/${id}' , user);
navigate.push("/");
};
const loadUser = async () =>{
const result = await axios.get('http://localhost:3001/users/${id}', user);
setUser(result.data);
};
return (
<div className="container">
<div className="w-75 mx-auto shadow p-5">
<h5 className="text-left mb-4"> Edit Organization </h5>
<form onSubmit={e => onSubmit(e)}>
<div className="form-group">
<label> Organization Name </label>
<input type="text" className="form-control" name="name" value={name}
onChange={e => onInputChange(e)}
/>
</div>
<div className="form-group">
<label> Parent Organization Name </label>
<input type="text" className="form-control" name="poname" value={poname}
onChange={e => onInputChange(e)}
/>
</div>
<div className="form-group">
<label> Type </label>
<input type="text" className="form-control" name="type" value={type}
onChange={e => onInputChange(e)}
/></div>
<div className="form-group">
<label> Organization Status </label>
<input type="text" className="form-control" name="ostatus" value={ostatus}
onChange={e => onInputChange(e)}
/></div>
<div className="container">
<div className="btn-6">
<button className="btn btn-danger float-right">Update</button>
</div>
</div>
</form>
</div>
</div>
);
}
Around line 26 you are closing the function too early.
useEffect(() => {
loadUser();
}, []);
}; // <- Move this to the very end
const onSubmit = async e => {
</div>
</div>
);
}; // <- Move to here
You mistakly add extra termination of curly bracket
useEffect(() => {
loadUser();
}, []);
}; // <--- remove this

Input field focus state in react not working with the given style

I have an input field in my react project to display a separate component when it's in a hover state. I have a separate component called [Focus Style] from the Actual component but all I'm doing is not working. The focus state should work with the first input field Kindly assist me. My code below.
Main Componenet
import React, { useState, useEffect } from "react";
import stays from "./Components/stays.json";
// import styled, {css} from "styled-components";
import FocusStyle from "../src/Components/FocusStyle";
export default function SearchGuest() {
const [guest, setGuest] = useState("");
const [location, setLocation] = useState("");
const [Data, setData] = useState([]);
const [filteredData, setFilteredData] = useState(Data);
const [focus, setFocus] = useState("false");
const onFocusChange = () => {
setFocus("true");
console.log("focused");
};
useEffect(() => {
setData(stays);
setFilteredData(stays);
}, []);
const handleSearch = (event) => {
event.preventDefault();
};
return (
<div>
<form action="" >
<input
onFocus={onFocusChange}
type="text"
name="guest"
id=""
// value={guest}
placeholder="text"
onChange={handleSearch}
style={focus ? { border: "1px solid yellow" } : <FocusStyle/> }
/>
<input
type="number"
name="location"
id=""
// value={location}
placeholder="number"
onChange={handleSearch}
/>
<button value="submit" >Search</button>
</form>
{console.log(filteredData)}
</div>
);
}
FocusStyle Component
import React from 'react'
export default function FocusStyle() {
return (
<div>
<h2 style={{marginTop: "90px", color: 'pink'}}>Focus Componenet</h2>
<div className="modal">
<div className="adult">
<span>Adult</span>
<button>-</button><span>0</span><button>+</button>
</div>
<div className="children">
<span>Children</span>
<button>-</button><span>0</span><button>+</button>
</div>
</div>
</div>
)
}
if so then you can do this
{focus ? <input
onFocus={onFocusChange}
type="text"
name="guest"
id=""
// value={guest}
placeholder="text"
onChange={handleSearch}
style={{ border: "1px solid yellow" }}
/>:
<FocusStyle/>
}

How to access the input value of children component from parents component in ReactJs?

I have a problem with the access of the input value, I used here is .map().
Here is the code, <QuestionLabel/>is the children component. projectsData.projectDetail is an available data
//..
{projectsData.projectDetail.questions.map((question) => (
<QuestionLabel
questionTitle={question}
/>
))}
//child component
const QuestionLabel=(props)=>{
const [answerInput, setAnswerInput] = React.useState("");
return(
<div className="contact">
<form className="contact-form" autocomplete="off">
<div class="contact-form-group">
<label
for="name"
class="contact-form-label"
>
{props.questionTitle}
</label>
<input
id="name"
type="text"
class="contact-form-input"
value={answerInput}
onChange={(answer) => setAnswerInput(answer.target.value)}
/>
</div>
</form>
</div>
);
}
export default QuestionLabel;
There are many way to get value from child component for parent component. You can call a function pass from parent to children to set value for parent state when it's changed. Exmaple:
const ParentComponent =(props)=>{
const [valueFromChild, setValueFromChild] = useState('');
return <>
{valueFromChild}
<QuestionLabel questionTitle={'Title'} setValueFromChild={setValueFromChild}/>
</>
}
const QuestionLabel=(props)=>{
const [answerInput, setAnswerInput] = React.useState("")
useEffect(() => {
props.setValueFromChild(answerInput);
}, [answerInput]);
return(
<div className="contact">
<form className="contact-form" autoComplete="off">
<div class="contact-form-group">
<label
for="name"
class="contact-form-label"
>
{props.questionTitle}
</label>
<input
id="name"
type="text"
class="contact-form-input"
value={answerInput}
onChange={(answer) => setAnswerInput(answer.target.value)}
/>
</div>
</form>
</div>
);
}
So you need to have the input state up in the parent.
Since you are mapping through questions array to render the QuestionLabel we can try this...
//..
const ParentComponent = () => {
const [answers, setAnswers] = React.useState({})
useEffect(() => {
projectsData.projectDetail.questions.forEach((_, i) => {
setAnswers(previousAnswers => {...previousAnswers, ["name" + i]: ""})
})
}, [])
const handleAnswers = (e) => {
setAnswers(previousAnswers => {...previousAnswers, [e.target.name]: [e.target.value]})
}
//..
then
//..
{
projectsData.projectDetail.questions.map((question, i) => {
return (
<QuestionLabel questionTitle={question} inputName={"name" + i} answers={answers} handleAnswers={handleAnswers} />
)
})
}
and finally...
//child component
const QuestionLabel=(props)=>{
return(
<div className="contact">
<form className="contact-form" autocomplete="off">
<div class="contact-form-group">
<label
for={props.inputName}
class="contact-form-label"
>
{props.questionTitle}
</label>
<input
id={props.inputName}
name={props.inputName}
type="text"
class="contact-form-input"
value={answers[props.inputName]}
onChange={handleAnswers}
/>
</div>
</form>
</div>
);
}
export default QuestionLabel;

How can I change render input to select in redux-form?

I work with forms through redux-form wuth react.js. I need some help with dependent dropdown fields in Dynamic form. I have react component form:
import React, { Component, PropTypes } from 'react'
import { reduxForm, addArrayValue } from 'redux-form'
import PureInput from '../forms/PureInput'
export const fields = [
'children[].type',
'children[].description',
'children[].value',
]
export const contactTypes = [{key:1,val:'E-mail'},{key: 2, val:"Phone"}, {key:3, val:"Social"}]
export const services = [{key:1, val:"VK"},{key:2, val:"Viber"}, {key: 3, val:"Telegram"}]
class ContactForm extends Component {
render() {
const {
addValue,
fields: {
children
},
handleSubmit,
invalid,
submitting
} = this.props
return (
<div className="container">
<form onSubmit={handleSubmit} >
<h3>Основная информация</h3>
<div className="form-group">
{!children.length && <div>No Children</div>}
{children.map((child, index) => (
<div key={index}>
<div>
<label>Child #{index + 1}</label>
<div>
<select className="form-control" field={child.type} onChange={()=> _log('changed')} >
{contactTypes.map(contactType => <option value={contactType.key} key={contactType.key}>{contactType.val}</option>)}
</select>
</div>
<div>
<PureInput type="text" placeholder="type" field={child.description}/>
</div>
<div>
<PureInput type="text" placeholder="description" field={child.value}/>
</div>
<div>
<button type="button" onClick={() => {
children.removeField(index) // remove from index
}}><i/> Remove
</button>
</div>
</div>
</div>
))}
</div>
<button type="button" onClick={() => children.addField()}><i/> addField</button>
</form>
</div>
)
}
}
ContactForm.propTypes = {
fields: PropTypes.object.isRequired,
handleSubmit: PropTypes.func.isRequired,
submitting: PropTypes.bool.isRequired
}
export default reduxForm({
form: 'ContactForm',
fields
}, undefined, {
addValue: addArrayValue // mapDispatchToProps (will bind action creator to dispatch)
})(ContactForm)
Example:
If the value of the select is child.type == contactTypes[2].key then I need to render the select with
services as options for it, but not like default PureInput with child.description.
How can I do that?

Categories