React Send State Value to Another Component - reactjs

I want to send State value to another component. They are at same directory. Basically I want to get "selectedvalue" from "Conversation.js" to "Openconversation.js".
Everything's fine but I just need to send that State value (selectedvalue) to Openconversation.js
./components/Conversation.js:
import React, {useState} from 'react'
import { ListGroup } from 'react-bootstrap'
import { useConversations } from '../contexts/ConversationsProvider';
export default function Conversations() {
const { conversations, selectConversationIndex } = useConversations()
**const [selectedvalue, setSelectedValue] = useState('')**
return (
<ListGroup variant="flush">
{conversations.map((conversation, index) => (
<ListGroup.Item
key={index}
action
onClick={() => {selectConversationIndex(index) && setSelectedValue(conversation.recipients.map(r => r.name).join(', '))} }
active={conversation.selected}
>
{conversation.recipients.map(r => r.name).join(', ')}
</ListGroup.Item>
))}
</ListGroup>
)
}
./components/Openconversation.js:
import React, { useState, useCallback, useEffect } from 'react'
import { Form, InputGroup, Button } from 'react-bootstrap'
import { useConversations } from '../contexts/ConversationsProvider';
import Axios from 'axios';
export default function OpenConversation() {
const [text, setText] = useState('');
const [curuser, setCurUser] = useState('');
const [oldchats, setOldChats] = useState([]);
const setRef = useCallback(node => {
if (node) {
node.scrollIntoView({ smooth: true })
}
}, [])
useEffect(() => {
Axios.get('http://localhost:3001/api/get').then((response) =>{
setOldChats(response.data);
})
}, [])
useEffect(()=>{
fetch('http://localhost:8000/api/current_user/', {
headers: {
Authorization: `JWT ${localStorage.getItem('token')}`
}
})
.then(res => res.json())
.then(json => {
setCurUser(json.id);
});
},[])
const { sendMessage, selectedConversation } = useConversations()
function handleSubmit(e) {
e.preventDefault()
sendMessage(
selectedConversation.recipients.map(r => r.id),
text,
selectedConversation.recipients.map(r => {
Axios.post("http://localhost:3001/api/insert", {
content: text,
sender: curuser,
recipient: r.id,
}).then(() => {
alert("saved on MySQL Database!");
});
})
)
setText('')
}
return (
<div className="d-flex flex-column flex-grow-1">
{selectedConversation.messages.map((message, index) => {
const lastMessage = selectedConversation.messages.length - 1 === index
return (
<div>
{oldchats.map(val => {
##### I NEED THAT SELECTEDVALUE DATA FROM CONVERSATION.JS ########
return (val.sender== curuser && val.recipient == selectedvalue) ?
<h2>{val.content}</h2>
:
<div></div>
})}
</div>
)
})}
</div>
</div>
<Form onSubmit={handleSubmit}>
<Form.Group className="m-2">
<InputGroup>
<Form.Control
as="textarea"
required
value={text}
onChange={e => setText(e.target.value)}
/>
<InputGroup.Append>
<Button type="submit">Send</Button>
</InputGroup.Append>
</InputGroup>
</Form.Group>
</Form>
</div>
)
}

It looks like in your Openconversation.js file you're not importing useContext from react and then assigning it to something like:
const conversationProviderContext = useContext(ConversationsProvider);
Also make sure that you're using the <ConversationsProvider.Provider/> component higher up above the <Openconversation/> component.
You should probably refer to the docs: https://reactjs.org/docs/context.html

Related

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

How to filter or search for services in another component (React)

NOTE: My Page Card component is working correctly. How can I filter the card page component in the Search component?
I'm new to react, and I don't quite understand how I can accomplish this task.
In the Search component I put it in a fixed way, as I can't filter a component using another.
The Code is summarized for ease.
Card Page
import React, {
useEffect,
useState
} from "react";
import classes from "./boxService.module.css";
import axios from "axios";
function BoxService() {
const [test, SetTest] = useState([]);
useEffect(() => {
axios
.get("http://localhost:8080/api/test")
.then((response) => {
SetTest(response.data);
})
.catch(() => {
console.log("Error!");
});
}, []);
return ({
test.map((test, key) => {
<div className={classes.box}
return (
<Grid item xs = {2} key={key} >
<div className={test.name} < div >
<p className={test.description}</p>
</Grid>
);
})}
);
}
export default BoxService;
Seach Page
import React, {
useState,
useEffect
} from "react";
import axios from "axios";
function Search() {
const [searchTerm, setSearchTerm] = useState("");
const [test, SetTest] = useState([]);
//Chamada API
useEffect(() => {
axios
.get("http://localhost:8080/api/test")
.then((response) => {
SetTest(response.data);
})
.catch(() => {
console.log("Error");
});
}, []);
return (
<div>
<input type = "text"
placeholder = "Search..."
onChange = {
(event) => {
setSearchTerm(event.target.value);
}
}/>
{
test.filter((val) => {
if (searchTerm === "") {
return val;
} else if (
val.nome.toLowerCase().includes(searchTerm.toLowerCase())
) {return val;}
}).map((val, key) => {
return ( <div className = "user"
key = {key} >
<p> {val.name} </p> </div>
);
})
} </div>
);
}
export default Search;
Here is an example of how it should/could look like:
import React from "react";
function SearchBox({ setSearchTerm, searchTerm }) {
const handleFilter = (e) => {
setSearchTerm(e.target.value);
};
return (
<>
filter
<input type="search" onChange={handleFilter} value={searchTerm} />
</>
);
}
export default function App() {
const [searchTerm, setSearchTerm] = React.useState("");
const [filteredResults, setFilteredResults] = React.useState([]);
const [results, setResults] = React.useState([]);
React.useEffect(() => {
const fetchdata = async () => {
const randomList = await fetch(`https://randomuser.me/api/?results=50`);
const data = await randomList.json();
const { results } = data;
setResults(results);
};
fetchdata();
}, []);
React.useEffect(() => {
const filterResults = results.filter((item) =>
item.name.last.toLowerCase().includes(searchTerm.toLowerCase())
);
setFilteredResults(filterResults);
}, [searchTerm, results]);
return (
<div className="App">
<SearchBox setSearchTerm={setSearchTerm} searchTerm={searchTerm} />
<div>
<ul>
{filteredResults.map(({ name }, idx) => {
return (
<li key={idx}>
{name.first} {name.last}
</li>
);
})}
</ul>
</div>
</div>
);
}

Debounce input does not read keyboard information

I need DebounceInput to read information from my keyboard, but when I type in Ervin for example, the whole list stays and not just Ervin as it used to work. I don't really know where I went wrong. I know that Debounce is supposed to wait until the user stops typing, but even after waiting a minute nothing changed.
This is my files:
DebounceInput:
import {DebounceInput} from 'react-debounce-input';
type Props ={
onChange:Function;
}
const Debounce:React.FC<Props> = ({onChange}) => {
return(
<DebounceInput
onChange={(e) => onChange(e.target.value)}
debounceTimeout={500}
className="SearchInput"
placeholder="Search by user name..."
/>
)
}
export default Debounce
UsersList:
import {UsersContext} from '../../contexts/Users'
type Props = {
filteredUsers:Array<Person>;
}
type Person = {
name:string;
username:string;
}
const UsersList: React.FC = () => {
const filteredUsers = useContext(UsersContext);
return(
<div className="ListHead">
<ol className="list">
{filteredUsers.map((Person) => (
<li key={Person.name}>
<span>{Person.name}</span>
#{Person.username}
</li>
))}
</ol>
</div>
)}
Users:
import React, {useState, useEffect} from 'react';
import axios from 'axios';
type Users = Person[];
type Person = {
name:string;
username:string;
}
export const UsersContext = React.createContext<Users>([]);
const UsersProvider:React.FC= ({children}) => {
const [users, setUsers] = useState<Person[]>([]);
useEffect(() => {
axios
.get(`https://jsonplaceholder.typicode.com/users`)
.then((response) =>{
setUsers(response.data);
})
.catch((error) => {
console.log(error);
});
},[]);
return (
<UsersContext.Provider value={users}>
{children}
</UsersContext.Provider>
)
}
And Main:
const Main:React.FC= () => {
const [showUser, setShowUser] = useState("");
return (
<div>
<div>
<Header/>
</div>
<UsersProvider>
<UsersList />
<Debounce onChange={setShowUser}/>
</UsersProvider>
</div>
)
}
Your debouncing Input does not do anything except for changing the showUser state. Since your users resides in the UsersProvider . You can pass the value of your showUser as a prop to UsersProvider .
const Main:React.FC= () => {
const [showUser, setShowUser] = useState("");
return (
<div>
<div>
<Header/>
</div>
<UsersProvider searchText={showUser}>
<UsersList />
<Debounce onChange={setShowUser}/>
</UsersProvider>
</div>
)
}
Now inside your UsersProvider you can do the following
type UsersProviderProps = {
children: React.ReactNode;
searchText: string;
}
export const UsersContext = React.createContext<Users>([]);
const UsersProvider:React.FC<UsersProviderProps>= ({children, searchText}) => {
const [users, setUsers] = useState<Person[]>([]);
useEffect(() => {
axios
.get(`https://jsonplaceholder.typicode.com/users`)
.then((response) =>{
setUsers(response.data);
})
.catch((error) => {
console.log(error);
});
},[]);
const filteredUsers = useMemo(() => {
if(searchText.trim().length > 0 && users.length > 0){
return users.filter((person) =>
person.name.toLowerCase().includes(searchText.toLowerCase())
);
} else {
return users;
}
}, [searchText, users])
return (
<UsersContext.Provider value={filteredUsers}>
{children}
</UsersContext.Provider>
)
}

Whenever I use any material ui icon, I am getting this error. And when I remove the material ui icon from my code, the error disappears

enter image description hereError: 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:
You might have mismatching versions of React and the renderer (such as React DOM)
You might be breaking the Rules of Hooks
You might have more than one copy of React in the same app
See https://reactjs.org/link/invalid-hook-cal
import React, { useEffect, useState } from 'react';
import "./AddKeywordsPage.css";
import './AddedKeywords';
import AddedKeywords from './AddedKeywords';
import { Link, useHistory } from 'react-router-dom';
import { db } from '../../firebase';
import firebase from 'firebase';
import FeedCreated from '../LeftMenuBar/FeedCreated';
import { useSelector } from 'react-redux';
import { selectUser } from '../features/userSlice';
function AddKeywordsPage({ toggle }) {
const [keywords, setKeywords] = useState([]);
const [input, setInput] = useState('');
const [feedInput, setFeedInput] = useState('');
const history = useHistory();
const user = useSelector(selectUser);
// useEffect(() => {
// db.collection('keywords').orderBy('timestamp', 'desc').onSnapshot((snapshot) =>
// setKeywords(
// snapshot.docs.map((doc) => (
// {
// id: doc.id,
// data: doc.data()
// }
// ))
// ))
// }, [])
useEffect(() => {
db.collection('keywords').orderBy('timestamp', 'desc').onSnapshot((snapshot) =>
setKeywords(
snapshot.docs.map((doc) => (
{
id: doc.id,
data: doc.data()
}
))
))
}, [])
const addKeywordTag = (e) => {
// e.preventDefault();
db.collection('keywords').add({
keyword: input,
timestamp: firebase.firestore.FieldValue.serverTimestamp()
});
setInput("");
};
const addFeed = (eve) => {
eve.preventDefault();
db.collection('feeds').add({
feed: feedInput,
timestamp: firebase.firestore.FieldValue.serverTimestamp()
});
console.log(setFeedInput);
setFeedInput("");
setKeywords([]);
alert("Feed added successfully");
}
return (
<div className="addkeywordspage_main">
<div className="addkeywordspage">
<div>
<p className="addkeywordspage_keywords">Create Feed</p>
<div className="addkeywordspage_feed">
<TextField
value={feedInput}
onChange={eve => setFeedInput(eve.target.value)}
style={{ width: '300px'}}
id="outlined-basic"
label="Add Feed Name"
variant="outlined"
InputLabelProps={{style: {fontSize: 15}}}
/>
</div>
<div className="addkeywordspage_flex">
<div className="addkeywordspage_textfield">
<TextField
value={input}
onChange={e => setInput(e.target.value)}
style={{ width: '400px' }}
id="outlined-basic"
label="Add phrases"
variant="outlined"
InputLabelProps={{style: {fontSize: 15}}}
/>
</div>
<div className="addkeywordspage_button1">
<button onClick={addKeywordTag}>Add</button>
</div>
</div>
<div className="over">
{keywords.map(({ id, data: { keyword } }) => (
<AddedKeywords
id={id}
keyword={keyword}
/>
))}
</div>
<div className="add_feed_button">
<button onClick={addFeed} >Add Feed</button>
</div>
</div>
</div>
</div>
)
}
export default AddKeywordsPage;
l for tips about how to debug and fix this problem.

Resources