How to output data from one component to another? - reactjs

Please help with the task. I have 2 components: Header, which has a Modal component (for a modal window) and a Main_Part component. I don't know how to make so that on the Create button values ​​from input were created in the Main_part component.
How can this be done? Thanks
Main_part.js
import React from "react";
import "./Main_Part.css";
export let Main_Part = ({newList}) => {
return (<div className="main">
</div>)
}
Header.js
import React, {useState} from "react";
import "./Header.css";
import {Modal} from "../Modal/Modal";
import {Main_Part} from "../Main_Part/Main_Part";
export let Header = () => {
let [modalActive, setModalActive] = useState(false);
let [newList, setNewList] = useState([]);
let [input, setInput] = useState('');
let addList = (myInput) => {
let newDiscussion = {
id: Math.random(),
task: myInput
}
setNewList([...newList, newDiscussion])
}
let handleInput = (e) => {
setInput(e.target.value)
}
let submitTask = (e) => {
e.preventDefault();
addList(input);
setInput('')
}
return (<header className="header">
<div>
<button onClick={() => setModalActive(true)}>Створити новий список</button>
</div>
<Modal active={modalActive} setActive={setModalActive}>
<form onSubmit={submitTask}>
<input
type="text"
placeholder="Створити"
onChange={handleInput}
/>
<button type="submit" onSubmit={submitTask}>Create</button>
</form>
<button onClick={() => setModalActive(false)}>Закрити</button>
</Modal>
<Main_Part newList={newList}/>
</header>)
}

Here's the simple sample of what you're wanting to do. You just need to pass the input values to Main_Part and check its for not null definition in Main_Part:
newList && newList.map((v, i) => v.task)
Here's the example:
import { useState } from "react";
import "./styles.css";
const Main_Part = ({ newList }) => {
return <div className="main">{newList && newList.map((v, i) => v.task)}</div>;
};
const Header = () => {
let [modalActive, setModalActive] = useState(false);
let [newList, setNewList] = useState([]);
let [input, setInput] = useState("");
let addList = (myInput) => {
let newDiscussion = {
id: Math.random(),
task: myInput
};
setNewList([...newList, newDiscussion]);
};
let handleInput = (e) => {
setInput(e.target.value);
};
let submitTask = (e) => {
e.preventDefault();
addList(input);
setInput("");
};
return (
<header className="header">
<form onSubmit={submitTask}>
<input type="text" placeholder="Створити" onChange={handleInput} />
<button type="submit" onSubmit={submitTask}>
Create
</button>
</form>
<button onClick={() => setModalActive(false)}>Закрити</button>
<Main_Part newList={newList} />
</header>
);
};
export default function App() {
return (
<>
<Header />
<Main_Part />
</>
);
}

Related

Delete currently clicked item in react

Thanks for the help in advance. Currently, I am learning react. As a part of it I am coding a project with a basic listing and deleting the currently clicked listed item. I was able to list the entered item but was not able to delete it. I was able to fetch the id of the currently clicked item but doesn't have a picture of what to do next.Can anyone please help me to solve this.
My code:
App.js
import React, { useState, Fragment } from "react";
import UserAddForm from "./UserAddForm/UserAddForm";
import UserList from "./UserList/UserList";
const App = () => {
const [dataList, setDataList] = useState([]);
const FormDatas = (datas) => {
setDataList([datas, ...dataList]);
};
const deleteListItem = (clickedListId) => {
console.log(clickedListId);
};
return (
<Fragment>
<UserAddForm enteredFormVals={FormDatas} />
<UserList listDatas={dataList} deleteItem={deleteListItem} />
</Fragment>
);
};
export default App;
UserList.js
import React from "react";
import userListing from "./UserList.module.css";
const UserList = (props) => {
const deleteHandler = (e) => {
console.log(e.target.id);
props.deleteItem(e.target.id);
};
return (
<div className={`${userListing.users} ${userListing.whiteBg}`}>
<ul>
{props.listDatas.map((data) => (
<li key={data.id} id={data.id} onClick={deleteHandler}>
{data.name}
{data.age}
</li>
))}
</ul>
</div>
);
};
export default UserList;
UserAddForm.js
import React, { useState } from "react";
import UserForm from "./UserAddForm.module.css";
const UserAddForm = (props) => {
const [enteredName, setEnteredName] = useState("");
const [enteredAge, setEnteredAge] = useState("");
const nameHandler = (e) => {
setEnteredName(e.target.value);
};
const ageHandler = (e) => {
setEnteredAge(e.target.value);
};
const userFormHandler = (e) => {
e.preventDefault();
const EnteredFormDatas = {
name: enteredName,
age: enteredAge,
id: Math.random().toString(),
};
props.enteredFormVals(EnteredFormDatas);
};
return (
<div className={`${UserForm.input} ${UserForm.whiteBg}`}>
<form onSubmit={userFormHandler}>
<label htmlFor="username">Username</label>
<input
id="username"
type="text"
onChange={nameHandler}
value={enteredName}
/>
<label htmlFor="age">Age (Years)</label>
<input
id="age"
type="number"
onChange={ageHandler}
value={enteredAge}
/>
<button type="submit" className={UserForm.button}>
Add User
</button>
</form>
</div>
);
};
export default UserAddForm;
You need to filter out the item from the array by keeping ones with a different id and set it back as a new dataList.
const deleteListItem = (clickedListId) => {
setDataList(items => items.filter(({ id }) => id !== clickedListId))
};

How can I display the result of the child component in the parent component?

my application consists of a filter select by provinces, the filter is a modal that shows me the result in the filter component (child), I would like to show the result in the parent not in the filter modal. I do not know what to pass to the parent to show the result or how to show it on the screen.
///parent component
import React, { useState, useEffect } from 'react'
import { useDispatch } from "react-redux";
import { getClinic } from '../../api/drupalAPI'
import {Clinic} from '#icofcv/common';
import { selectClinics } from '../../actions/detailClinics'
import { useNavigate } from "react-router-dom";
import contentUtils from '../../lib/contentUtils'
import { SearchFilterClinics } from './SearchFilterClinics'
import Button from 'react-bootstrap/Button';
import Form from 'react-bootstrap/Form';
const ClinicList = () => {
const [clinicList, setClinicList] = useState<Clinic[]>([]);
const [clinicListFiltered, setClinicListFiltered] = useState<Clinic[]>([]);
const [searchClinic, setSearchClinic] = useState("");
const dispatch = useDispatch();
const navigate = useNavigate();
///modal control
const [isOpen, setIsOpen] = useState(false);
const openModal = () => setIsOpen(true);
const closeModal = () => setIsOpen(false);
console.log(searchClinic);
const fetchClinicList = async () => {
getClinic().then((response)=>{
console.log(response)
setClinicList(response);
setClinicListFiltered(response)
}).catch ( (error) => {
console.error(error);
throw error;
});
}
const handleChange=e=>{
setSearchClinic(e.target.value);
filter(e.target.value);
}
const filter=(termSearch)=>{
const resultSearch= clinicList.filter((element)=>{
if(element.title.toString().toLowerCase().includes(termSearch.toLowerCase())
){
return element;
}
});
setClinicListFiltered(resultSearch);
}
function handleAddToDetail(clinic) {
dispatch(selectClinics(clinic));
navigate('clinicdetail');
}
function goToPageSearchFilterClinics() {
navigate('filterclinics');
}
useEffect (() => {
fetchClinicList();
}, []);
return(
<>
<div style={{display: 'flex'}}>
<div style={{width:'5rem'}}>
{/* <button onClick={() => goToPageSearchFilterClinics()}>filtro</button> */}
<button onClick={openModal}>filtro</button>
</div>
< SearchFilterClinics isOpen={isOpen} closeModal={closeModal} clinicFilter={clinicFilter}></SearchFilterClinics>
<Form className="d-flex">
<Form.Control
type="search"
value={searchClinic}
placeholder="Search"
className="me-2"
aria-label="Search"
onChange={handleChange}
/>
</Form>
</div>
<div className="content-cliniclist">
{
clinicListFiltered.map((clinic) => (
<div style={{marginBottom: '3rem'}}>
<button
type="button"
onClick={() => handleAddToDetail(clinic)}
style={{all: 'unset'}}
>
<div>
{/* <img src={ contentUtils.getLargeImageUrl(clinic.logoWidth )} alt="#"></img> */}
<div>{clinic.title}</div>
<div>{clinic.propsPhone}</div>
<div>{clinic.mobile}</div>
<div>{clinic.email}</div>
<div>{clinic.registry}</div>
</div>
</button>
</div>
))
}
</div>
</>
)
}
export default ClinicList;
////child component
import React, { useState, useEffect } from 'react'
import Select, { SingleValue } from 'react-select'
import { getClinic } from '../../api/drupalAPI'
import {Clinic} from '#icofcv/common';
import "./Modal.css";
interface Props {
isOpen: boolean,
clinicFilter: String,
closeModal: () => void
}
export const SearchFilterClinics : React.FC<Props> = ({ children, isOpen, closeModal, clinicFilter }) => {
////filter
type OptionType = {
value: string;
label: string;
};
const provincesList: OptionType[] = [
{ value: 'Todos', label: 'Todos' },
{ value: 'Valencia', label: 'Valencia' },
{ value: 'Castellon', label: 'Castellon' },
{ value: 'Alicante', label: 'Alicante' },
]
const [clinicList, setClinicList] = useState<Clinic[]>([]);
const [clinicListFilteredSelect, setClinicListFilteredSelect] = useState<Clinic[]>([]);
const [filterSelectClinic, setFilterSelectClinic] = useState<SingleValue<OptionType>>(provincesList[0]);
const handleChangeSelect = async (provinceList: SingleValue<OptionType>) => {
getClinic().then((response) => {
setClinicList(response);
setClinicListFilteredSelect(response)
setFilterSelectClinic(provinceList);
filterSelect(provinceList );
}).catch ((error) => {
console.error(error);
throw error;
});
}
const filterSelect=(termSearch)=>{
const resultFilterSelect = clinicList.filter((element) => {
if(element.province?.toString().toLowerCase().includes(termSearch.value.toLowerCase() )
){
return element;
}
});
setClinicListFilteredSelect(resultFilterSelect);
}
const handleModalContainerClick = (e) => e.stopPropagation();
return (
<>
<div className={`modal ${isOpen && "is-open"}`} onClick={closeModal}>
<div className="modal-container" onClick={handleModalContainerClick}>
<button className="modal-close" onClick={closeModal}>x</button>
{children}
<div>
<h1>Encuentra tu clínica</h1>
</div>
<div>
<form>
<label>Provincia</label>
<Select
defaultValue={filterSelectClinic}
options={provincesList}
onChange={handleChangeSelect}
/>
</form>
{
clinicListFilteredSelect.map((clinicFilter) => (
<div>
<div>{clinicFilter.title}</div>
<div>{clinicFilter.propsPhone}</div>
<div>{clinicFilter.mobile}</div>
<div>{clinicFilter.email}</div>
<div>{clinicFilter.province} </div>
<div>{clinicFilter.registry}</div>
</div>
))
}
</div>
</div>
</div>
</>
)
}
You need to pass a callback function to the parent and set the state value in the parent, not in the child. (This is known as lifting state up.)
In your case this would involve moving
const [filterSelectClinic, setFilterSelectClinic] = useState<SingleValue<OptionType>>(provincesList[0]);
Into the parent component. Then passing the setFilterSelectClinic function into the child component.
<SearchFilterClinics setFilterSelectClinic={(value) => setFilterSelectClinic(value)} isOpen={isOpen} closeModal={closeModal} clinicFilter={clinicFilter}/>
The value (within the parenthesis) is being passed up by the child component. This is the value you set here:
getClinic().then((response) => {
...
// this is the function we pass in from the parent. It set's the value
// of the callback function to provinceList
setFilterSelectClinic(provinceList);
...
We then setFilterSelectClinic to that value. Meaning filterSelectClinic now has the value passed up in the callback.

Set a default content from database in react-draft-wysiwyg editor

I am creating a blog website in which I am embedding react-draft-wysiwyg editor. I am facing problem when the user has to update the blog. When I click the update button the content is gone. I looked into many solutions but I couldn't make it work.
This is my code
import axios from "axios";
import React, { useContext, useEffect, useState } from "react";
import { useLocation } from "react-router";
import { Link } from "react-router-dom";
import { Context } from "../../context/Context";
import "./singlePost.css";
import { EditorState, ContentState, convertFromHTML } from 'draft-js';
import { Editor } from 'react-draft-wysiwyg';
import { convertToHTML } from 'draft-convert';
import DOMPurify from 'dompurify';
import 'react-draft-wysiwyg/dist/react-draft-wysiwyg.css';
import Parser from 'html-react-parser';
export default function SinglePost() {
const location = useLocation();
const path = location.pathname.split("/")[2];
const [post, setPost] = useState({});
const PF = "http://localhost:5000/images/";
const { user } = useContext(Context);
const [title, setTitle] = useState("");
const [desc, setDesc] = useState("");
const [updateMode, setUpdateMode] = useState(false);
useEffect(() => {
const getPost = async () => {
const res = await axios.get("/posts/" + path);
setPost(res.data);
setTitle(res.data.title);
setDesc(res.data.desc);
};
getPost();
}, [path]);
const handleDelete = async () => {
try {
await axios.delete(`/posts/${post._id}`, {
data: { username: user.username },
});
window.location.replace("/");
} catch (err) {}
};
// updating post
const handleUpdate = async () => {
try {
await axios.put(`/posts/${post._id}`, {
username: user.username,
title,
desc,
});
setUpdateMode(false)
} catch (err) {}
};
const [editorState, setEditorState] = useState(
() => EditorState.createWithContent(
ContentState.createFromBlockArray(
convertFromHTML(desc)
)
),
);
const [convertedContent, setConvertedContent] = useState(null);
const handleEditorChange = (state) => {
setEditorState(state);
convertContentToHTML();
}
const convertContentToHTML = () => {
let currentContentAsHTML = convertToHTML(editorState.getCurrentContent());
setConvertedContent(currentContentAsHTML);
setDesc(currentContentAsHTML);
}
const createMarkup = (html) => {
return {
__html: DOMPurify.sanitize(html)
}
}
return (
<div className="singlePost">
<div className="singlePostWrapper">
{post.photo && (
<img src={PF + post.photo} alt="" className="singlePostImg" />
)}
{updateMode ? (
<input
type="text"
value={title}
className="singlePostTitleInput"
autoFocus
onChange={(e) => setTitle(e.target.value)}
/>
) : (
<h1 className="singlePostTitle">
{title}
{post.username === user?.username && (
<div className="singlePostEdit">
<i
className="singlePostIcon far fa-edit"
onClick={() => setUpdateMode(true)}
></i>
<i
className="singlePostIcon far fa-trash-alt"
onClick={handleDelete}
></i>
</div>
)}
</h1>
)}
<div className="singlePostInfo">
<span className="singlePostAuthor">
Author:
<Link to={`/?user=${post.username}`} className="link">
<b> {post.username}</b>
</Link>
</span>
<span className="singlePostDate">
{new Date(post.createdAt).toDateString()}
</span>
</div>
{updateMode ? (
// <textarea
// className="singlePostDescInput"
// value={desc}
// onChange={(e) => setDesc(e.target.value)}
// />
<Editor
contentState={desc}
editorState={editorState}
onEditorStateChange={handleEditorChange}
wrapperClassName="wrapper-class"
editorClassName="editor-class"
toolbarClassName="toolbar-class"
/>
) : (
<p className="singlePostDesc">{Parser(desc)}</p>
)}
{updateMode && (
<button className="singlePostButton" onClick={handleUpdate}>
Update
</button>
)}
</div>
</div>
);
}
I want to display desc which is saved in MongoDB database when the user clicks on update button.
The following part is what I tried to do but didn't work.
const [editorState, setEditorState] = useState(
() => EditorState.createWithContent(
ContentState.createFromBlockArray(
convertFromHTML(desc)
)
),
);
I am getting warning in this:
react.development.js:220 Warning: Can't call setState on a component that is not yet mounted. This is a no-op, but it might indicate a bug in your application. Instead, assign to this.state directly or define a state = {}; class property with the desired state in the r component.
Please help

React useState hook - I want to render certain component when only press submit button not when onChange

I'm new to react.
Now trying to make form with react hooks, and I want to render Cloud component only when press submit button. But it rendered every onChange called.
I know that onChange re-rendered cause also useState hook.
But have no idea how to render only when press submit button.
My final goal is when write name and press enter, if value is not contained in api, setShake make shake True and if True, put shake-cloud class in Cloud.js.
REACT IS TOO DIFFICULT :(
Thanks for help tho :)
App.js
import React, { useState, useEffect } from "react";
import "./App.css";
import { FontAwesomeIcon } from "#fortawesome/react-fontawesome";
import { faSearch } from "#fortawesome/free-solid-svg-icons";
import "./search.css";
import PageTitle from "./component/PageTitle";
import Cloud from "./component/Cloud";
import Loading from "./component/Loading";
//https://api.color.pizza/v1/
//data.colors[0].name
const App = () => {
const [isLoading, setIsLoading] = useState(false);
const [colorNames, setColorNames] = useState("");
const [search, setSearch] = useState("");
const [query, setQuery] = useState("");
const [cloudHex, setCloudHex] = useState("ivory");
const [shake, setShake] = useState(false);
useEffect(() => {
getColorLists();
}, []);
const getColorLists = async () => {
const res = await fetch(`https://api.color.pizza/v1/`);
const data = await res.json();
await setColorNames(data);
setIsLoading(true);
};
const isColor = () => {
let makeUpper =
query.search(/\s/) == -1
? query.charAt(0).toUpperCase() + query.slice(1)
: query
.split(" ")
.map((i) => i.charAt(0).toUpperCase() + i.slice(1))
.join(" ");
for (let i = 0; i < colorNames.colors.length; i++) {
if (colorNames.colors[i].name == makeUpper) {
setCloudHex(colorNames.colors[i].hex);
return;
} else if (i == colorNames.colors.length - 1) {
return makeShake();
}
}
};
const updateSearch = (e) => {
setSearch(e.target.value);
};
const getSearch = (e) => {
e.preventDefault();
setQuery(search);
isColor();
};
const makeShake = async () => {
await setShake(true)
await setShake(false)
}
return (
<>
{!isLoading ? (
<Loading />
) : (
<div className="App">
<div className="app-wrap">
<PageTitle />
<div className="search-wrap">
<form onSubmit={getSearch} className="search-form">
<input
className="search-bar"
type="text"
value={search}
onChange={updateSearch}
/>
<button type="submit" className="search-button">
<FontAwesomeIcon
icon={faSearch}
className="search"
/>
</button>
</form>
</div>
<Cloud cloudhex={cloudHex} shake={shake} />
</div>
</div>
)}
</>
);
};
export default App;
Cloud.js
import React, {useEffect} from "react";
import { FontAwesomeIcon } from "#fortawesome/react-fontawesome";
import { faCloud } from "#fortawesome/free-solid-svg-icons";
import './cloud.css';
const Cloud = ({cloudhex, shake}) => {
useEffect(() => {
}, [])
console.log(shake)
return (
<div className={`cloud-wrap ${ shake ? "shake-cloud":''}`}>
<span className="cloudhexname">{cloudhex}</span>
<FontAwesomeIcon icon={faCloud} className="cloud" style={{color:`${cloudhex}`}} />
</div>
);
};
export default Cloud;
A good approach in this case is to use useRef() Hook to store our search field value, instead of using useState(). Because useRef() Hook does not force a re-render while useState() does. This approach is known as un-controlled way to use input field.
You basically need to make few modifications in your code which are as follows:
const search = useRef("");
Then remove onChange={updateSearch} and value={search} from input and use a property ref={search}. So that your input looks like below:
<input
className="search-bar"
type="text"
ref={search}
/>
Then in the submit handler, you can get the value of the input field using search.current.value. So your getSearch() would look like
const getSearch = (e) => {
e.preventDefault();
setClicked(true);
setQuery(search.current.value);
isColor();
};
Assuming user has typed an input. If not then you can set a validation before using setQuery() in the getSearch() form submit handler.
if(search.current.value){
setQuery();
}
Note: If you have any other controlled inputs in your project, you can change then to un-controlled inputs using refs and this way re-renders would not happen in your code.
Do it like that
If you want to render cloud component after form submit then put one flag and toggle that, here I take clicked state
import React, { useState, useEffect } from "react";
import "./App.css";
import { FontAwesomeIcon } from "#fortawesome/react-fontawesome";
import { faSearch } from "#fortawesome/free-solid-svg-icons";
import "./search.css";
import PageTitle from "./component/PageTitle";
import Cloud from "./component/Cloud";
import Loading from "./component/Loading";
//https://api.color.pizza/v1/
//data.colors[0].name
const App = () => {
const [isLoading, setIsLoading] = useState(false);
const [colorNames, setColorNames] = useState("");
const [search, setSearch] = useState("");
const [query, setQuery] = useState("");
const [cloudHex, setCloudHex] = useState("ivory");
const [shake, setShake] = useState(false);
const [clicked, setClicked] = useState(false);
useEffect(() => {
getColorLists();
}, []);
const getColorLists = async () => {
const res = await fetch(`https://api.color.pizza/v1/`);
const data = await res.json();
await setColorNames(data);
setIsLoading(true);
};
const isColor = () => {
let makeUpper =
query.search(/\s/) == -1
? query.charAt(0).toUpperCase() + query.slice(1)
: query
.split(" ")
.map((i) => i.charAt(0).toUpperCase() + i.slice(1))
.join(" ");
for (let i = 0; i < colorNames.colors.length; i++) {
if (colorNames.colors[i].name == makeUpper) {
setCloudHex(colorNames.colors[i].hex);
return;
} else if (i == colorNames.colors.length - 1) {
return makeShake();
}
}
};
const updateSearch = (e) => {
setSearch(e.target.value);
};
const getSearch = (e) => {
e.preventDefault();
setClicked(true);
setQuery(search);
isColor();
};
const makeShake = async () => {
await setShake(true)
await setShake(false)
}
return (
<>
{!isLoading ? (
<Loading />
) : (
<div className="App">
<div className="app-wrap">
<PageTitle />
<div className="search-wrap">
<form onSubmit={getSearch} className="search-form">
<input
className="search-bar"
type="text"
value={search}
onChange={updateSearch}
/>
<button type="submit" className="search-button">
<FontAwesomeIcon
icon={faSearch}
className="search"
/>
</button>
</form>
</div>
{clicked && <Cloud cloudhex={cloudHex} shake={shake} />}
</div>
</div>
)}
</>
);
};
export default App;

handleSubmit functional is not working in functional component of React

I am trying to convert class component of todo app into functional component. Everything goes well, but when I submit the form, the blank screen appears. I think there is some issue in handleSubmit function. Please help.
import React, {useState} from "react";
export const TodoFunc = (props: Props) => {
const [items, setItems] = useState([])
const [text, setText] = useState('')
const handleChange = (e) => {
setText(e.target.value)
}
const handleSubmit = (e) => {
e.preventDefault()
if (text.length === 0) {
return;
}
const newItems = {text: {text}, id: Date.now()}
setItems(() => (items.concat(newItems)))
setText('')
}
return (
<div>
<h3>TODO</h3>
<TodoList items = {items} />
<form onSubmit={handleSubmit}>
<label htmlFor="new-todo">
What do you want to do?
</label>
<input type="text"
id='new-todo'
onChange={handleChange}
value={text}
/>
<button>
Add #{items.length + 1}
</button>
</form>
</div>
);
};
const TodoList = (props) => {
return (
<ul>
{props.items.map(item => <li key={item.id}>{item.text}</li>)}
</ul>
)
}
Your problem lies in the below line:
const newItems = {text: {text}, id: Date.now()}
Here you are assigning an object to the text key and not just the value of the variable text.
And this is why when you loop over them in your TodoList component you are not able to display any of them.

Resources