I need to make an update method in my application. But I'm not able to filter the specific id of the task to put as a parameter of the request. If I use filter/map it returns the two indices of the array. But through my click, I want to filter the id that I clicked. How can I do this?
import Modal from 'react-modal'
import { useMutation } from 'react-query'
import ReactQuill from 'react-quill'
import { toast } from 'react-toastify'
import { useForm } from '../../hooks/useForm'
import { useTasks } from '../../hooks/useTasks'
import { api } from '../../services/api'
import { queryClient } from '../../services/queryClient'
import { modules } from '../../utils/modules'
import { ButtonContainer, CancelButton, SaveButton } from './styles'
type UpdateNoteModalProps = {
isOpen: boolean
onRequestClose: () => void
}
type UpdateNoteData = {
id: string
form?: { [key: string]: string | number }
}
export const UpdateNoteModal = ({
isOpen,
onRequestClose
}: UpdateNoteModalProps) => {
const { data } = useTasks()
const { form, handleInputChange } = useForm({
initialState: {
description: ''
}
})
const id = data?.filter((note: any) => note._id)
// .filter((note: any) => note.id === note.id)
// const id = findId.find((note: any) => note.id === note.id)
console.log({ id })
const updateNote = useMutation(
async ({ id, form }: UpdateNoteData) => {
const response = await api.put(`/task/${id}`, form)
const token = localStorage.getItem('token')
if (token) {
api.defaults.headers.common.Authorization = token
}
return response.data
},
{
onSuccess: () => {
queryClient.invalidateQueries('task')
onRequestClose()
toast.success('🦄 Sua nota foi atualizada com sucesso!', {
position: 'top-center',
autoClose: 5000
})
},
onError: () => {
toast.error('🦄 Ocorreu um erro, tente novamente mais tarde!', {
position: 'top-center',
autoClose: 5000
})
}
}
)
const handleSubmit = async () => {
// event.preventDefault()
await updateNote.mutateAsync({ id, form })
}
return (
<Modal
isOpen={isOpen}
onRequestClose={onRequestClose}
className="react-modal-content"
overlayClassName="react-modal-overlay"
>
<h2>Editar</h2>
<ReactQuill
modules={modules}
theme="snow"
className="toolbar"
onChange={handleInputChange}
/>
{/* <input
type="text"
name="description"
value={form.description}
onChange={handleInputChange}
/> */}
<ButtonContainer>
<div onClick={onRequestClose}>
<CancelButton>Cancelar</CancelButton>
</div>
<div>
<SaveButton onClick={() => handleSubmit()}>Salvar</SaveButton>
</div>
</ButtonContainer>
</Modal>
)
}
Maybe you can do something like:
const index = data.map(x => x.id).indexOf(x === id)
And then you can use that index to get the data piece you want e.g. const foundItem = data[index]
Related
There is a component for uploading files with drag and drop, here is its code:
import clsx from 'clsx';
import React, { DragEvent, useEffect, useState } from 'react';
import { Link } from 'react-router-dom';
import { Delete, DocumentDownloadIcon } from '#oam/shared/assets';
import { Button, ButtonType } from '../button/button';
export interface UploadFieldProps extends React.InputHTMLAttributes<HTMLInputElement> {
label?: string;
mainErrorMessage?: string;
subErrorMessage?: string;
onFilesChange?: (files: FileList | File[] | null) => void;
multiple?: boolean;
inputId?: string;
defaultFiles?: DefaultFiles[];
route?: string;
showDelete?: boolean;
maxSize?: number;
deleteHandler?: (id: string, files?: File[]) => void;
showDownloadIcon?: boolean;
}
export interface DefaultFiles {
fileId: string;
name: string;
}
const getBorderClasses = (disabled: boolean): string =>
clsx({
'absolute right-0 float-right pr-2 -z-10 -top-5': true,
'cursor-pointer': !disabled
});
const getInputClasses = (className: string, disabled: boolean): string =>
clsx({
'block w-full pt-4 pb-5 text-sm p-2 mt-0 bg-transparent border-dashed border border-oam-gray-500 appearance-none':
true,
[className]: !!className,
'cursor-pointer hover:border-oam-blue-light': !disabled
});
const MAX_SIZE = 5242880;
const WORD_FORMAT =
'application/msword,application/vnd.openxmlformats-officedocument.wordprocessingml.document';
const EXCEL_FORMAT = 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet';
interface IFile extends File {
fileId?: string;
}
export function UploadField({
className = '',
inputId = 'upload-input',
disabled = false,
label = 'Drag files here or ...',
mainErrorMessage,
subErrorMessage,
multiple = false,
onFilesChange,
defaultFiles = [],
route,
showDelete = true,
maxSize = MAX_SIZE,
deleteHandler,
showDownloadIcon,
...props
}: UploadFieldProps) {
const [errorMessage, setErrorMessage] = useState('');
const [files, setFiles] = useState<FileList | File[] | null | Array<File>>(
defaultFiles as unknown as File[]
);
const deleteUploadedFile = (file: IFile) => {
if (files && !disabled) {
const arrayFiles = Array.from(files).filter((f) => f.name !== file.name);
if (file.fileId) {
deleteHandler?.(file.fileId, arrayFiles);
}
setFiles(arrayFiles);
}
};
useEffect(() => {
setFiles(defaultFiles as unknown as File[]);
}, [defaultFiles]);
const createDataTransferFiles = (filesChanged: FileList) => {
const dataTransfer = new DataTransfer();
setErrorMessage('');
Array.from(filesChanged).forEach((file) => {
if (file.size <= maxSize) {
if (dataTransfer.files.length !== 0) {
for (const fileData of Array.from(dataTransfer.files)) {
if (fileData.name !== file.name) {
dataTransfer.items.add(file);
break;
}
}
} else {
dataTransfer.items.add(file);
}
} else {
setErrorMessage(`${file.name} exceeds size limit. 5mb max file size supported.`);
}
});
setFiles([...Array.from(dataTransfer.files), ...(files as [])]);
return dataTransfer.files;
};
const stopDragAndDropDefaultEvent = (e: DragEvent<HTMLDivElement>) => {
e.preventDefault();
e.stopPropagation();
};
const onDragAndDrop = (e: DragEvent<HTMLDivElement>) => {
if (disabled) {
return;
}
stopDragAndDropDefaultEvent(e);
const files = e.dataTransfer.files;
if (files && files.length) {
const fileList = createDataTransferFiles(e.dataTransfer.files);
onFilesChange && onFilesChange(fileList);
}
};
const onChange = (event: React.ChangeEvent<HTMLInputElement>) => {
if (event.target.files) {
const fileList = createDataTransferFiles(event.target.files);
onFilesChange && onFilesChange(fileList);
}
};
return (
<div
className='relative z-0 w-full'
onDragOver={stopDragAndDropDefaultEvent}
onDragEnter={stopDragAndDropDefaultEvent}
onDrop={onDragAndDrop}
>
<input
id={inputId}
accept={`image/*,.pdf,${WORD_FORMAT},${EXCEL_FORMAT},application/JSON`}
type='file'
data-testid='upload-input'
className='hidden'
multiple={multiple}
disabled={disabled}
onChange={onChange}
{...props}
/>
{label &&
<label htmlFor='upload-input' className={getInputClasses(className, disabled)}>
{label}
</label>
}
<div className={getBorderClasses(disabled)}>
<div className='flex items-center'>
<Button
disabled={disabled}
data-testid='add-file'
title='Upload file'
buttonType={ButtonType.SECONDARY}
/>
</div>
</div>
{files &&
<div className='pt-2'>
{Array.from([...(files as unknown as File[])]).map((file, index) => {
return (
<div className='flex items-center content-center p-2 mb-3' key={index}>
{file?.name &&
<>
{defaultFiles[index]?.fileId ?
<Link to={`${route + defaultFiles[index].fileId}`} className='flex font-bold'>
{file?.name}
{showDownloadIcon &&
<div className='pl-8 pr-2'>
<DocumentDownloadIcon data-testid='download-file' />
</div>
}
</Link>
:
<span className='pr-8 font-bold'>{file?.name}</span>
}
{showDelete && !disabled &&
<Delete
data-testid='delete-file'
onClick={() => deleteUploadedFile(file)}
className='justify-end w-4 h-4 cursor-pointer text-oam-gray-600'
/>
}
</>
}
</div>
);
})}
</div>
}
{(!!mainErrorMessage || !!errorMessage) &&
<div className='flex flex-row ml-2 text-sm text-status-red' id='error'>
<span className='pr-2 font-bold'>{mainErrorMessage || errorMessage}</span>
<span>{subErrorMessage}</span>
</div>
}
</div>
);
}
export default UploadField;
And on a page of the application I'm using the above component, inside a Controller of react-hook-form, there is a React-Query hook for uploading the file which works fine, the only issue is that in console there is this error message that runs forever, like 100 times a second, the counter keeps growing very fast.
Here is the page where I'm using the above component:
import { yupResolver } from '#hookform/resolvers/yup';
import { useCallback, useEffect, useState } from 'react';
import { Controller, useForm } from 'react-hook-form';
import { object, mixed } from 'yup';
import { useToggle } from '#oam/shared/hooks';
import { UploadField } from '#oam/shared/ui-components';
import { ILicense } from '../../constants/types';
import { useUploadLicense } from '../../hooks';
export const DEFAULT_FILE = object({
attachments: mixed().nullable().notRequired()
});
export function License() {
const { uploadFiles } = useUploadLicense();
const defaultValues = DEFAULT_FILE.cast({
attachments: []
});
const form = useForm({
resolver: yupResolver(DEFAULT_FILE),
defaultValues
});
const [uploadedFile, setUploadedFile] = useState<FileList | File[] | null>(null);
const [parsedFile, setParsedFile] = useState<ILicense>();
const [validLicense, toggleValidLicense] = useToggle(false);
const [invalidLicense, toggleInvalidLicense] = useToggle(false);
const [invalidFormat, toggleInvalidFormat] = useToggle(false);
const parseFile = useCallback(async () => {
if (invalidFormat) {
toggleInvalidFormat();
}
if (uploadedFile?.length) {
if (uploadedFile[0].type !== 'application/json') {
toggleInvalidFormat();
} else {
const uploadedFileText = await uploadedFile[0].text().then((text) => {
return text;
});
const parsed = JSON.parse(uploadedFileText);
setParsedFile(parsed);
form.handleSubmit(async (data) => {
const files = data.attachments;
const filesResponse = await uploadFiles(
{ files },
{
onSuccess: () => toggleValidLicense(),
onError: () => toggleInvalidLicense()
}
).catch((e) => form.setError('attachments', { message: e }));
form.setValue('attachments', filesResponse?.data);
})();
}
}
}, [
form,
invalidFormat,
toggleInvalidFormat,
toggleInvalidLicense,
toggleValidLicense,
uploadFiles,
uploadedFile
]);
useEffect(() => {
if (uploadedFile?.length) {
parseFile();
}
}, [parseFile, uploadedFile]);
return (
<div>
<Controller
control={form.control}
name='attachments'
render={() =>
<UploadField
onFilesChange={(files) => {
form.setValue('attachments', files);
setUploadedFile(files);
if (validLicense && files?.length) {
toggleValidLicense();
}
if (invalidLicense && files?.length) {
toggleInvalidLicense();
}
if (invalidFormat && files?.length) {
toggleInvalidFormat();
}
}}
/>
}
/>
</div>
);
}
export default License;
And the React-Query hook but I don't think it has a connection to the bug:
import { useMutation, useQueryClient } from 'react-query';
import { IUploadFieldRequest } from '../constants/types';
import { licenseServiceAPI } from '../services';
export const URI = () => '/licenses/upload';
export const useUploadLicense = () => {
const queryClient = useQueryClient();
const [apiService] = licenseServiceAPI<FormData>({
headers: {
'Content-Type': 'multipart/form-data'
}
});
const {
data: response,
mutateAsync,
isLoading,
isSuccess
} = useMutation(
(formData: IUploadFieldRequest) => {
const form = new FormData();
Array.from(formData.files)?.forEach((file) => {
form.append('license-file', file);
});
return apiService.post(URI(), form);
},
{
onError: () => {
return queryClient.invalidateQueries('licenseUpload');
}
}
);
return {
data: response?.data,
uploadFiles: mutateAsync,
isLoading,
isSuccess
};
};
export default useUploadLicense;
The erros that keeps appearing:
Warning: Maximum update depth exceeded. This can happen when a component calls setState inside useEffect, but useEffect either doesn't have a dependency array, or one of the dependencies changes on every render.
Any ideas how to fix this?
I try to build sliders with different categories that each user has his point.
The informant comes from the json server
What I need I do not succeed in having the customer choose a user that is numbered and the dot will be colored in the slider How do I do that?
In addition he has the option to delete and return the point.
I was able to delete the points by deleting them in the object. But I could not return, is there a possibility to return?
Broker.jsx
import React, { useEffect, useState } from 'react';
import './style.css';
import Combo from '../components/Combo/Combo';
import Sliders from '../components/Sliders/Sliders';
const GetUsersDataFromManipulation = (users, field) => {
const state = users.reduce((store, user) => {
const userId = user.user
const currentManipulationUserData = user.profileManipulation[field]
if (currentManipulationUserData.length === 0) {
return store
}
store[userId] = currentManipulationUserData[0].bid
return store;
}, {})
return state;
};
function Broker({ manipulations }) {
const users = manipulations[2].users
const [hiddenUser, setHiddenUser] = useState(() => {
const visible = {};
for (let user of users) {
visible[user.user] = true;
}
return visible;
})
const GetUsersBid = (profileManipulation) => {
const data = GetUsersDataFromManipulation(users, `${profileManipulation}`); if (!Object.keys(data).length) {
return null
}
return data;
};
const gender = GetUsersBid('gender');
const age = GetUsersBid('age');
const marital = GetUsersBid('marital');
const children = GetUsersBid('children');
const education = GetUsersBid('education');
const interests = GetUsersBid('interests');
const dynamicInterests = GetUsersBid('dynamicInterests');
const showUser = (user_id) => {
const new_hidden = { ...hiddenUser }
new_hidden[user_id] = true;
setHiddenUser(new_hidden);
}
const hideUser = (user_id) => {
const new_hidden = { ...hiddenUser }
console.log(user_id)
new_hidden[user_id] = false;
setHiddenUser(new_hidden);
}
const [userInformation, setUserInformation] = useState([
{ name: 'gender', bids: gender },
{ name: 'age', bids: age },
{ name: 'marital', bids: marital },
{ name: 'children', bids: children },
{ name: 'education', bids: education },
{ name: 'interests', bids: interests },
{ name: 'dynamicInterests ', bids: dynamicInterests },
]);
useEffect(() => {
const curret_User_Info = [...userInformation]
for (let user of Object.keys(hiddenUser)) {
for (let i = 0; i < curret_User_Info.length; i++) {
if (curret_User_Info[i].bids !== null) {
if (hiddenUser[user] === false) {
delete curret_User_Info[i].bids[user]
}
else {
//What am I returning here? So that the bids will return?
}
}
}
}
setUserInformation(curret_User_Info)
}, [hiddenUser])
return (
<div>
<div className="button" >
{userInformation && <Combo users={users} showUser={showUser} hideUser={hideUser} userInformation={userInformation} />}
</div>
<div className='slid'>
{userInformation.map(sliderDetails => {
return (
<div className={sliderDetails.name} key={sliderDetails.name} >
{sliderDetails.bids && (<Sliders className="sliders" hiddenUserChange={hiddenUser} name={sliderDetails.name} userBids={sliderDetails.bids} setUserInformation={setUserInformation} userInformation={userInformation} />)}
</div>
)
})}
</div>
</div>
);
}
export default Broker;
ComboBox.jsx
import React, { useEffect, useRef, useState } from 'react';
import ComboBox from 'react-responsive-combo-box';
import { Button } from '#mui/material';
import 'react-responsive-combo-box/dist/index.css';
import "./style.css"
function Combo({ users, showUser, hideUser, userInformation }) {
const [selectedOption, setSelectedOption] = useState();
const [choosing, setChoosing] = useState();
useEffect(() => {
}, [users])
const onShow = () => {
showUser(users[selectedOption - 1].user)
}
const onHide = () => {
hideUser(users[selectedOption - 1].user)
}
const colorChange = (numOption) => {
const id = users[numOption - 1].user
}
return (
<div className="combo_box">
<ComboBox
onSelect={(option) => { setSelectedOption(option); colorChange(option) }}
options={[...Array.from({ length: users.length }, (_, i) => i + 1)]}
/>
<div className='button' >
<Button style={{ "marginRight": 20 }} variant="contained" onClick={onShow}>Show</Button>
<Button variant="contained" onClick={onHide}>Hide</Button>
</div>
</div>
);
}
export default Combo;
Sliders.jsx
import React, { useEffect, useState } from 'react'
import "./style.css"
import 'rc-slider/assets/index.css';
import Slider from 'rc-slider';
const Sliders = ({ hiddenUserChange, name, userBids, setUserInformation, userInformation }) => {
const [bids, setBids] = useState()
useEffect(() => {
setBids(Object.values(userBids))
}, [hiddenUserChange, userBids])
const updateFieldChanged = (newValue, e) => {//OnChanged Slider
setUserInformation(state => {
return state.map(manipulation => {
if (manipulation.name === name) {
Object.entries(manipulation.bids).forEach(([userId, bidValue], index) => {
manipulation.bids[userId] = newValue[index]
console.log(manipulation.bids[userId])
})
}
return manipulation
})
});
}
const handleChange = (event, newValue) => {
setBids(event)
};
return (
<>
<h1 className='headers'>{name}</h1>
{
<Slider
style={{ "marginRight": "20rem", "width": "30rem", "left": "20%" }}
range={true}
trackStyle={[{ backgroundColor: '#3f51b5' }]}
max={100}
RcSlider={true}
railStyle={{ backgroundColor: '#3f51b5' }}
activeDotStyle={{ left: 'unset' }}
ariaLabelForHandle={Object.keys(hiddenUserChange)}
tabIndex={(Object.keys(userBids))}
ariaLabelledByForHandle={bids}
value={(bids)}
onChange={handleChange}
onAfterChange={updateFieldChanged}
tipProps
tipFormatter
/>
}
</>
)
}
export default Sliders
enter image description here
Thank you all!
I am pushing to an array
options.push(
{
value: <Adrress x={edge.node.location.lon} y={edge.node.location.lat} />,
label: <Adrress x={edge.node.location.lon} y={edge.node.location.lat} />
})
But always When I need this array it refreshes and I get at the start - '' and then actual data, I need to get once to interact with its.
export default function DataFteching({ x, y }) {
const [adrress, setAdrress] = useState(null)
const [loading, setLoading] = useState(true)
const region = /place/
useEffect(() => {
async function FtechData() {
const token = 'pk.eyJ1IjoiYW5kcmlpbXNuIiwiYSI6ImNrZGYzZ200YTJudXQyeHNjMjk2OTk2bjUifQ.njqMX6x6U946yjJdWwA7mA';
await axios.get(`https://api.mapbox.com/geocoding/v5/mapbox.places/${x},${y}.json?access_token=${token}`)
.then(res => {
// console.log(res.data.features.find(place => place.id.match(region)).text)
setAdrress(res.data)
})
.catch(err => console.log(err))
.finally(() => setLoading(false));
}
FtechData();
}, [])
if (loading) return false;
// console.log({ adrress.features.find(place => place.id.match(region)).text })
console.log(`${(adrress.features.find(place => place.id.match(region)).text)}`)
return `${(adrress.features.find(place => place.id.match(region)).text)}`
}
The Code Sandbox have dependencies error but the solution could be something like this
import React, { useEffect, useState } from "react";
import { Link, graphql, useStaticQuery } from "gatsby";
import Layout from "../components/layout";
import blogStyles from "./Styles/blog.module.scss";
import Head from "../components/head";
import Adrress from "../components/Map/ftechAdrress";
import WithCallbacks from "../components/Search-panel";
const BlogPage = () => {
const [options, setOptions] = useState([]);
const data = useStaticQuery(graphql`
query {
allContentfulBlogPost(
sort: { fields: publishedDate, order: DESC }
filter: { node_locale: { eq: "en-US" } }
) {
edges {
node {
title
slug
publishedDate(formatString: "MMM Do, YYYY")
image {
file {
url
}
}
location {
lat
lon
}
breed
find
}
}
}
}
`);
useEffect(() => {
const tmp = [];
data.allContentfulBlogPost.edges.forEach((edge) => {
tmp.push({
value: (
<Adrress x={edge.node.location.lon} y={edge.node.location.lat} />
),
label: <Adrress x={edge.node.location.lon} y={edge.node.location.lat} />
});
});
setOptions([...tmp]);
}, [data]);
return (
<Layout>
<Head title="Blog" />
<h1>lost pets</h1>
{console.log(options)}
<WithCallbacks options={options} />
<ol className={blogStyles.posts}>
{options.map((edge) => {
const styles = edge.node.image
? `url("${edge.node.image.file.url}")`
: `url("https://cdn.pixabay.com/photo/2019/07/30/05/53/dog-4372036__340.jpg")`;
return (
<li>
<Link to={`/blog/${edge.node.slug}`}>
<div
style={{ backgroundColor: "pink", backgroundImage: styles }}
>
<h2>{edge.node.title}</h2>
<p>{edge.node.publishedDate}</p>
<p>Порода: {edge.node.breed}</p>
<p>
Статус: <span>{edge.node.find ? "Найден" : "Потерян"}</span>
</p>
<p>
city:{" "}
<Adrress
x={edge.node.location.lon}
y={edge.node.location.lat}
/>
</p>
</div>
</Link>
</li>
);
})}
</ol>
</Layout>
);
};
export default BlogPage;
const options = []
const region = /place/
data.allContentfulBlogPost.edges.forEach(async (edge) => {
await fetch(`https://api.mapbox.com/geocoding/v5/mapbox.places/${edge.node.location.lon},${edge.node.location.lat}.json?access_token=${token}`)
.then(response => response.json())
.then(json => options.push({
value: json.features.find(place => place.id.match(region)).text,
label: json.features.find(place => place.id.match(region)).text
}))
console.log(options)
}
)
I'm trying to create small app based on Json server package which will help me to remember movies I want to watch when I have free time, want to learn React and Axios so I'm doing it with these technologies , Idea is when I click on add movie button - movie will be added to Json database,
when click on delete - particular movie will be deleted
and when click on the list - I will be able to edit text,
Delete works if I do something like http://localhost:3000/movies/1, to show what id should it delete, but is there any way to set it? To delete the list connected to button I'm clicking at? something like http://localhost:3000/movies/"id"? I will be grateful for any help as I totally don't have any idea how to move on with it
import React from 'react';
import ReactDom from 'react-dom';
import axios from 'axios';
import List from "./list.jsx";
class Form extends React.Component {
constructor(props) {
super(props)
this.state = {
name:'',
type:'',
description:'',
id:'',
movies: [],
}
}
handleChangeOne = e => {
this.setState({
name:e.target.value
})
}
handleChangeTwo = e => {
this.setState({
type:e.target.value
})
}
handleChangeThree = e => {
this.setState({
description:e.target.value
})
}
handleSubmit = e => {
e.preventDefault()
const url = `http://localhost:3000/movies/`;
axios.post(url, {
name: this.state.name,
type: this.state.type,
description:this.state.description,
id:this.state.id
})
.then(res => {
// console.log(res);
// console.log(res.data);
this.setState({
movies:[this.state.name,this.state.type,this.state.description, this.state.id]
})
})
}
handleRemove = (e) => {
const id = this.state.id;
const url = `http://localhost:3000/movies/`;
// const id = document.querySelectorAll("li").props['data-id'];
e.preventDefault();
axios.delete(url + id)
.then(res => {
console.log(res.data);
})
.catch((err) => {
console.log(err);
})
}
// editMovie = e => {
// const url = `http://localhost:3000/movies/`;
// e.preventDefault();
// const id = e.target.data("id");
// axios.put(url + id, {
// name: this.state.name,
// type: this.state.type,
// description:this.state.description,
// })
// .then(res => {
// console.log(res.data);
// })
// .catch((err) => {
// console.log(err);
// })
// }
render() {
return (
<form onSubmit={this.handleSubmit}>
<input type="text" placeholder="movie" onChange={this.handleChangeOne}/>
<input type="text" placeholder="type of movie" onChange={this.handleChangeTwo}/>
<textarea cols={40} rows={5} placeholder="description of the movie" onChange={this.handleChangeThree}></textarea>
<input type="submit" value="Add movie"></input>
<List removeClick={this.handleRemove} editClick={this.editMovie}/>
</form>
)
}
}
export default Form
List:
import React from 'react';
import ReactDom from 'react-dom';
import axios from 'axios';
class List extends React.Component{
constructor(props){
super(props)
this.state = {
movies: [],
}
}
componentDidMount() {
const url = `http://localhost:3000/movies`;
console.log(url);
axios.get(url)
.then(res => {
console.log(res.data);
const movies = res.data;
this.setState({
movies: movies
})
})
.catch((err) => {
console.log(err);
})
}
// editMovie =(e) => {
// console.log("it works with edit!");
// if (typeof this.props.editClick === "function") {
// this.props.editClick(e)
// } else {
// console.log("Doesn't work with edit");
// }
// }
removeMovie =(e) => {
console.log("it works with remove!");
if (typeof this.props.removeClick === "function") {
this.props.removeClick(e)
} else {
console.log("Doesn't work with remove");
}
}
render(){
let movies = this.state.movies.map(e =>
<ul onClick={this.editMovie}>
<li data-id={e.id}>
{e.name}
</li>
<li data-id={e.id}>
{e.type}
</li>
<li data-id={e.id}>
{e.description}
</li>
<button type="submit" onClick={this.removeMovie}>Delete</button>
</ul>)
return(
<div>
{movies}
</div>
)
}
}
export default List;
Json part
{
"movies": [
{
"id": 1,
"name": "Kongi",
"type": "drama",
"description": "movie about monkey"
},
{
"id": 2,
"name": "Silent Hill",
"type": "thriller",
"description": "movie about monsters"
},
{
"name": "Harry potter",
"type": "fantasy",
"description": "movie about magic and glory",
"id": 3
}
]
}
You could pass the movie object to the removeMovie function in your List component and pass that to the this.props.removeClick function. You could then take the id of the movie to use for your request, and remove the movie from state if the DELETE request is successful.
Example
class Form extends React.Component {
handleRemove = movie => {
const url = `http://localhost:3000/movies/${movie.id}`;
axios
.delete(url)
.then(res => {
this.setState(previousState => {
return {
movies: previousState.movies.filter(m => m.id !== movie.id)
};
});
})
.catch(err => {
console.log(err);
});
};
// ...
}
class List extends React.Component {
removeMovie = (e, movie) => {
e.preventDefault();
if (this.props.removeClick) {
this.props.removeClick(movie);
}
};
// ...
render() {
return (
<div>
{this.state.movies.map(movie => (
<ul onClick={this.editMovie}>
<li data-id={movie.id}>{movie.name}</li>
<li data-id={movie.id}>{movie.type}</li>
<li data-id={movie.id}>{movie.description}</li>
<button type="submit" onClick={e => this.removeMovie(e, movie)}>
Delete
</button>
</ul>
))}
</div>
);
}
}
An simple example using hooks:
const URL = 'https://jsonplaceholder.typicode.com/users'
const Table = () => {
const [employees, setEmployees] = React.useState([])
React.useEffect(() => {
getData()
}, [])
const getData = async () => {
const response = await axios.get(URL)
setEmployees(response.data)
}
const removeData = (id) => {
axios.delete(`${URL}/${id}`).then(res => {
const del = employees.filter(employee => id !== employee.id)
setEmployees(del)
})
}
const renderHeader = () => {
let headerElement = ['id', 'name', 'email', 'phone', 'operation']
return headerElement.map((key, index) => {
return <th key={index}>{key.toUpperCase()}</th>
})
}
const renderBody = () => {
return employees && employees.map(({ id, name, email, phone }) => {
return (
<tr key={id}>
<td>{id}</td>
<td>{name}</td>
<td>{email}</td>
<td>{phone}</td>
<td className='opration'>
<button className='button' onClick={() => removeData(id)}>Delete</button>
</td>
</tr>
)
})
}
return (
<>
<h1 id='title'>React Table</h1>
<table id='employee'>
<thead>
<tr>{renderHeader()}</tr>
</thead>
<tbody>
{renderBody()}
</tbody>
</table>
</>
)
}
ReactDOM.render(<Table />, document.getElementById('root'));
I am experiencing a very weird react behavior. There is this component which gets message and emailSubmitedText. In render method based on some condition it should render either first or the other one.
Now at first it is message. I click on the submit of the form. All the functions happen.
The component rerenders. In the console log I can see this time it should render emailSubmitedText. In react devtools it show the right text.
However in the actual html and html inspector it still shows the previos text.
import React, { Component } from 'react'
import PropTypes from 'prop-types'
import Details from './Details'
class DefaultMessage extends Component {
inputRef = null
renderEmailForm = () => {
return (
<form
className='b2c_email-form input-field'
onSubmit={e => {
e.preventDefault()
const { projectId, visitSessionId } = this.props
this.setState({ email: this.inputRef.value })
this.props.onSubmitEmail({
email: this.inputRef.value,
convertedPage: window.location.href, projectId, visitSessionId
})
}}
>
<div className="input-field">
<input ref={elem => this.inputRef = elem} id='email' type='email' className='validate' value={this.props.email} />
<label htmlFor='email' data-error='Invalid email address'>E-mail</label>
</div>
<button
className='b2c_email-form-button waves-effect waves-light btn'
type='submit'
style={{
backgroundColor: this.props.companyColor || '#63bc78'
}}
>Submit</button>
</form>
)
}
render = () => {
console.log('...> ', this.props.error || !this.props.contactId && this.props.message || this.props.emailSubmitedText)
return (
<div className='b2c_chat-message'>
<Details
classNames='b2c_chat-message-details__admin'
avatar={this.props.avatar}
name={this.props.name}
date={this.props.date}
/>
<div className='b2c_chat-message-text b2c_chat-message-text__admin b2c_chat-message-default'>
<div className='b2c_chat-message-after b2c_chat-message-after__admin' />
{this.props.error || !this.props.contactId && this.props.message || this.props.emailSubmitedText}
{!this.props.contactId && this.renderEmailForm()}
</div>
</div>
)
}
}
DefaultMessage.propTypes = {
projectId: PropTypes.string.isRequired,
visitSessionId: PropTypes.string.isRequired,
name: PropTypes.string.isRequired,
email: PropTypes.string.isRequired,
date: PropTypes.string.isRequired,
message: PropTypes.string.isRequired,
onSubmitEmail: PropTypes.func.isRequired
}
export default DefaultMessage
Here is the direct parent of the component.
import React, { Component } from 'react'
import PropTypes from 'prop-types'
import $ from 'jquery'
import moment from 'moment'
import randomstring from 'randomstring'
import DefaultMessage from './DefaultMessage'
import Message from './Message'
import UserTypingIndicator from '../UserTypingIndicator'
import TypingIndicator from './TypingIndicator'
class Messages extends Component {
chatRef = null
componentDidUpdate () {
this.scrollToTheLastMessage()
}
scrollToTheLastMessage = () => {
const $chat = $(this.chatRef)
const scrollTop = $chat.prop('scrollHeight') - $chat.innerHeight()
$chat.scrollTop(scrollTop)
}
renderDefaultMessage = () => (
<DefaultMessage
contactId={this.props.contactId}
companyColor={this.props.companyColor}
error={this.props.error}
date={moment().format('h:mm A')}
name={this.props.adminName}
avatar={this.props.adminAvatar}
message={this.props.welcomeMessage}
emailSubmitedText={this.props.emailSubmitedText}
projectId={this.props.projectId}
visitSessionId={this.props.visitSessionId}
onSubmitEmail={this.props.onSubmitEmail}
/>
)
renderMessages = () => {
let checkConversationDate = null
const {messages, contactName, adminName, adminAvatar} = this.props
const compareConversationDates = (createdAt) => {
checkConversationDate = moment(createdAt).format("DD.MM.YYYY")
return (
<div key={randomstring.generate()} className='conversationDayDate'>
<span>{checkConversationDate}</span>
</div>
)
}
if (!messages) return null
return messages.map((message, index) => {
return (
<div>
{checkConversationDate !== moment(message.createdAt.$date).format("DD.MM.YYYY") ? compareConversationDates(message.createdAt.$date) : ''}
{/* {index === 0 ? this.renderDefaultMessage() : ''} */}
<Message
isAdmin={message.userId ? true : false}
imageFile={message.imageFile}
key={randomstring.generate()}
companyColor={this.props.companyColor}
contactName={contactName}
adminName={adminName}
avatar={adminAvatar}
message={message.message}
date={moment(message.createdAt.$date).format('h:mm A')}
/>
</div>
)
})
}
renderTypingIndicators = () => {
const arrayToRender = [
this.props.isAdminTyping && <AdminTypingIndicator />,
this.props.isUserTyping && <UserTypingIndicator />
]
return arrayToRender
}
render = () => <div ref={elem => this.chatRef = elem} id='chat' className='chat-body' style={{
height: 'calc(100% - 190px - 3rem)',
overflowY: 'scroll',
margin: '30px 10px 10px 0',
boxSizing: 'border-box'
}}>
{this.renderDefaultMessage()}
{this.renderMessages()}
{this.renderTypingIndicators()}
</div>
}
Messages.propTypes = {
projectId: PropTypes.string.isRequired,
visitSessionId: PropTypes.string.isRequired,
messages: PropTypes.array.isRequired,
adminName: PropTypes.string.isRequired,
contactName: PropTypes.string.isRequired,
onSubmitEmail: PropTypes.func.isRequired
}
export default Messages
And here is where Container with states
import React, { Component } from 'react'
import Sound from 'react-sound'
import ddp from '../../ddp'
import Cookies from 'js-cookie'
import randomstring from 'randomstring'
import ChatContainer from './ChatContainer'
import Icon from './Icon'
import { connect, makeArrayCollectionFromObjectCollection, getNewMessages } from '../../functions'
class View extends Component {
defaultDocumentTitle = null
state = {
contactId: '',
chat: null,
show: false,
newMessagesCount: null,
notStatus: 'STOPPED'
}
newMessageNotification = newMessages => {
if (newMessages.length && newMessages.length > this.state.newMessagesCount) {
this.setState({ notStatus: 'PLAYING' })
document.title = `(${newMessages.length}) ${this.defaultDocumentTitle}`
} else if (!newMessages.length) {
document.title = this.defaultDocumentTitle
}
if (this.state.newMessagesCount !== newMessages.length) {
this.setState({ newMessagesCount: newMessages.length })
}
}
componentWillMount () {
this.defaultDocumentTitle = document.title
}
componentDidMount = async () => {
this.setContactIdFromCookies()
await connect(ddp)
}
setContactIdFromCookies = () => {
window.Cookies = Cookies
console.warn('setContactIdFromCookies')
const contactId = Cookies.get('b2cContactId')
console.log('contactId', contactId)
if (contactId) this.setState({contactId})
}
componentDidUpdate () {
console.warn('componentDidUpdate', this.props)
if (this.state.contactId && !this.state.chat) {
this.getChat(this.state.contactId)
}
if (this.state.chat && this.state.chat.length) {
let newMessages = getNewMessages(this.state.chat)
this.newMessageNotification(newMessages)
}
}
componentWillReceiveProps = (nextProps) => {
console.warn('componentWillReceiveProps', nextProps)
if (!nextProps.contactId) return
if (this.state.chat == null) this.getChat(nextProps.contactId)
}
getChat = async (contactId) => {
console.log('getChat', contactId)
await ddp.subscribe('Messages', {contactId})
const messagesColl = ddp.getCollection('Messages')
console.log('messagesColl', messagesColl)
this.setState({chat: this.getMessages(messagesColl)})
ddp.watch('Messages', (changedDoc, message) => {
console.log('Messages collection item changed', changedDoc, message)
const messagesColl = ddp.getCollection('Messages')
this.setState({chat: this.getMessages(messagesColl)})
})
}
getMessages = collection => {
let messages = []
if (collection) {
messages = makeArrayCollectionFromObjectCollection(collection)
}
console.log('messages', messages)
return messages
}
submitEmail = ({ email, convertedPage, projectId, visitSessionId }) => ddp.call('chat.init', { email, convertedPage, projectId, visitSessionId })
.then(contactId => {
Cookies.set('b2cContactId', contactId, { expires: 90 })
this.setState({ contactId, error: '' })
})
.catch(error => {
console.error('Error >', error)
})
readMessages = () => ddp.call('readMessages', {contactId: this.state.contactId, userId: !null})
.then(res => {
console.log('res', res)
})
.catch(error => {
console.error('Error', error)
})
submitMessage = ({message, visitSessionId, imageFile}) => ddp.call('chat.submitContactMessage', { message, visitSessionId, contactId: this.state.contactId, projectId: this.props.projectId, imageFile: imageFile || null})
.then((res) => {
console.log('res', res)
})
.catch(error => {
console.error('Error', error)
this.setState({error})
})
toggleChat = () => this.setState((state) => ({show: !state.show}))
sendFileToServer = (base64File, resolve, reject) => {
ddp.call('uploadToDropbox', base64File)
.then((res) => {
this.submitMessage({message: '', visitSessionId: this.props.visitSessionId, imageFile: res})
console.log('res', res)
})
.catch(error => {
console.error('Error', error)
})
}
getBase64 = (file, resolve, reject) => {
const self = this
const reader = new FileReader()
reader.readAsDataURL(file)
reader.onload = function () {
self.sendFileToServer(reader.result, resolve, reject)
}
reader.onerror = function (error) {
console.error('FileReader Error: ', error)
}
}
onFileDrop = files => {
let self = this
files.forEach(file => {
return new Promise((resolve, reject) => {
self.getBase64(file, resolve, reject)
})
})
}
render () {
return (
<div>
<ChatContainer
onFileDrop={this.onFileDrop}
contactId={this.state.contactId}
show={this.state.show}
error={this.state.error && <span style={{color: 'red'}}>{this.state.error}</span>}
chatSettings={this.props.chatSettings}
projectId={this.props.projectId}
visitSessionId={this.props.visitSessionId}
defaultAdminUserName='default defaultAdminUserName'
contactName='You'
supportName='Our Support'
messages={this.state.chat}
onSend={this.submitMessage}
onSubmitEmail={this.submitEmail}
toggleChat={this.toggleChat}
readMessages={this.readMessages}
/>
<Icon
companyColor={this.props.chatSettings.companyColor}
onClick={this.toggleChat}
newMessagesCount={this.state.newMessagesCount}
/>
<Sound
url='https://www.incredo.co/hubfs/b2c/Not%201.wav'
playStatus={this.state.notStatus}
playFromPosition={0}
onFinishedPlaying={() => this.setState({ notStatus: 'STOPPED' })}
/>
</div>
)
}
}
export default View