I'm currently getting a 'setAvailability is not defined' error, and I don't understand why as I haven't referenced setAvailability anywhere!
I need to set an initial value for availability, only if it is set.
Here's the code I currently have:
import React from "react";
import { TimePicker, Form, Button } from "antd";
import dayjs from "dayjs";
import { useDispatch, useSelector } from "react-redux";
const Availibility = () => {
const dispatch = useDispatch();
const { availibility: initAvailibility } = useSelector((state) => state.admin);
const initialValues = {
availibility: [
dayjs(initAvailibility?.[0]).format("HH:mm"),
dayjs(initAvailibility?.[1]).format("HH:mm"),
],
};
const onFinish = (values) => {
const from = dayjs(values.from).toDate();
const to = dayjs(values.to).toDate();
if (from > to) {
throw new Error("From date must be before to date");
}
// dispatch(setAvailibility({ from, to }));
};
return (
<div>
<Form onFinish={onFinish} initialValues={initialValues}>
<Form.Item name={"availibility"}>
<TimePicker.RangePicker format={"HH:mm"} minuteStep={60} />
</Form.Item>
<Form.Item>
<Button type="primary" htmlType="submit">
Submit
</Button>
</Form.Item>
</Form>
</div>
);
};
export default Availibility;
I'm so fed up with antd documentation, it's seriously lacking.
Anyway, thanks for any help
Related
Hello I am making an application to practice React, my notes app has a pagination which works perfectly, the problem is in the search engine, which only looks for notes from the page I'm on, for example, if I'm on page 2 and I look for a note on page 2, it shows it, however if the note is on a different page, it doesn't show it, it doesn't find it.
I know where the problem is but I'm not sure how to solve it, I'm a bit new to React and I was asking for your help.
I was able to do my pagination with the package react-paginate here is the documentation https://www.npmjs.com/package/react-paginate
My code:
Component App.js
import { useState, useEffect } from "react";
import { nanoid } from 'nanoid';
import './App.css';
import Search from "./components/Search";
import Header from "./components/Header";
import Pagination from "./components/Pagination";
const App = () => {
const [notes, setNotes] = useState([]);
const [searchText, setSearchText] = useState('');
const [darkMode, setDarkMode] = useState(false);
const [showNote, setShowNote] = useState(true); //eslint-disable-line
useEffect(() => {
const saveNotes = JSON.parse(localStorage.getItem('notes-data'));
if (saveNotes){
setNotes(saveNotes);
}
}, []);
useEffect(() => {
localStorage.setItem('notes-data', JSON.stringify(notes))
},[notes])
const addNote = (inputText, text) => {
const date = new Date();
const newNote = {
id: nanoid(),
title: inputText,
text: text,
date: date.toLocaleString()
}
const newNotes = [newNote, ...notes];
setNotes(newNotes)
}
const deleteNote = (id) => {
var response = window.confirm("Are you sure?");
if (response){
const notesUpdated = notes.filter((note) => note.id !== id)
setNotes(notesUpdated);
}
}
return (
<div className={darkMode ? 'dark-mode' : ''}>
<div className="container">
<Header
handleToggleTheme={setDarkMode}
/>
<Search
handleSearchNote={setSearchText}
setShowNote={setShowNote}
/>
<Pagination
data={notes}
handleAddNote={addNote}
handleDeleteNote={deleteNote}
searchText={searchText}
/>
</div>
</div>
)
}
export default App;
Component Pagination.js
import React, { useEffect, useState } from 'react'
import ReactPaginate from 'react-paginate';
import '../styles/Pagination.css';
import NoteList from './NoteList';
import { MdSkipPrevious, MdSkipNext } from 'react-icons/md';
const Pagination = (props) => {
const { data, searchText, handleAddNote, handleDeleteNote } = props;
// We start with an empty list of items.
const [currentItems, setCurrentItems] = useState([]);
const [pageCount, setPageCount] = useState(0);
// Here we use item offsets; we could also use page offsets
// following the API or data you're working with.
const [itemOffset, setItemOffset] = useState(0);
const itemsPerPage = 9;
useEffect(() => {
// Fetch items from another resources.
const endOffset = itemOffset + itemsPerPage;
console.log(`Loading items from ${itemOffset} to ${endOffset}`);
setCurrentItems(data.slice(itemOffset, endOffset));
setPageCount(Math.ceil(data.length / itemsPerPage));
}, [itemOffset, itemsPerPage, data]);
// Invoke when user click to request another page.
const handlePageClick = (event) => {
const newOffset = (event.selected * itemsPerPage) % data.length;
console.log(
`User requested page number ${event.selected}, which is offset ${newOffset}`
);
setItemOffset(newOffset);
};
return (
<>
<NoteList
notes={currentItems.filter((noteText) =>
noteText.title.toLowerCase().includes(searchText)
)}
handleAddNote={handleAddNote}
handleDeleteNote={handleDeleteNote}
/>
<div className="pagination-wrapper">
<ReactPaginate
breakLabel="..."
nextLabel={<MdSkipNext
className='icons'
/>}
onPageChange={handlePageClick}
pageRangeDisplayed={3}
pageCount={pageCount}
previousLabel={<MdSkipPrevious
className='icons'
/>}
renderOnZeroPageCount={null}
containerClassName="pagination"
pageLinkClassName="page-num"
previousLinkClassName="page-num"
nextLinkClassName="page-num"
activeLinkClassName="activee boxx"
/>
</div>
</>
);
}
export default Pagination;
Component NoteList.js
import React from 'react'
import Note from './Note'
import '../styles/NoteList.css'
import AddNote from './AddNote'
const NoteList = ({ notes, handleAddNote, handleDeleteNote }) => {
return (
<>
<div className="add-notes-wrapper">
<AddNote
handleAddNote={handleAddNote}
/>
</div>
<div className='notes-list'>
{notes.map((note =>
<Note
key={note.id}
id={note.id}
title={note.title}
text={note.text}
date={note.date}
handleDeleteNote={handleDeleteNote}
/>
))}
</div>
</>
)
}
export default NoteList;
Component Search.js
//import React, { useState } from 'react'
import {MdSearch, MdAdd} from 'react-icons/md'
import '../styles/Search.css'
const Search = ({ handleSearchNote, setShowNote }) => {
const handleShowAddNote = () => {
if (setShowNote){
let addNote = document.querySelector('.new');
addNote.classList.add('wobble-horizontal-top')
addNote.style.display='flex';
document.querySelector('.notes-list').style.display='none';
document.querySelector('.pagination').style.display='none';
}
}
return (
<div className='search'>
<div className="input-wrapper">
<MdSearch
className='icon search-icon'
/>
<input
type="text"
placeholder='What note are you looking for?'
onChange={(event) => handleSearchNote(event.target.value) }
/>
</div>
<div className="btn-wrapper-search">
<button
className='btn-addNote'
onClick={handleShowAddNote}>
Nueva Nota
</button>
<MdAdd
className='icon add-icon'
/>
</div>
</div>
)
}
export default Search
The problem is in the component Pagination.js because I'm filtering the notes on each page with the currentItems variable, if I did it with the data variable it would work, but then it would show all the notes, and I don't want that, I currently want to show 9 notes per page.
greetings and thanks in advance.
Edit:
#Newbie I'm doing what you said, but I don't know if you mean this, in my Pagination.js component I did:
useEffect(() => {
const filterNotes=data.filter((noteText) =>
noteText.title.toLowerCase().includes(searchText)
)
setItemOffset(0);
}, [data, searchText])
It doesn't work, do I have to pass a prop to my components additionally?
greetings.
As I suggested to you, search all the notes with searchText in your App.js and pass the results into the Pagination component and it will solve your problem.
Codesandbox: https://codesandbox.io/s/youthful-thompson-xugs0c
Edit
All changes are as per what we talked about in the email.
Codesandbox: https://codesandbox.io/s/green-fast-3k76wx
Search and pagination do not play well together, one of the common solutions is to jump to page 1 each time the filter term changes.
So use an useEffect on searchText to filter data and reset itemOffset to 0, then redo pagination as if the data changed.
The user will jump to page 1 at each keystroke of the search, then he can navigate pages (if there are more than one). This will lead to a less confusing UX.
I'm getting a placeholder value through props in my input component and I need to send the input value back to the main class. I'm using React but I'm not getting it. Follow my code.... The value I need to send is the value of 'usuario'
import React, { useState } from 'react';
import { EntradaDados } from './styled';
const PesqDados = ({placeholder, usuario}) => {
const [usuario, SetUsuario] = useState('')
const setValor =(e)=>{
SetUsuario(e.target.value);
}
console.log(usuario);
return(
<EntradaDados
onChange={setValor}
placeholder={placeholder}
>
</EntradaDados>
);
}
export default PesqDados;
You need to add a callback prop (onUsuarioChange) to your PesqDados component and call it with the new usuario. You have two options:
Call it from a useEffect with usuario as dependency (assuming usuario could get updated from somewhere other than setValor.
Call it from setValor, assuming that's the only place where usuario is going to get updated from.
This is how this should look:
import React, { useState } from 'react';
import { EntradaDados } from './styled';
const PesqDados = ({
placeholder,
usuario,
onUsuarioChange
}) => {
const [usuario, setUsuario] = useState('');
// Option 1:
useEffect(() => {
onUsuarioChange(usuario);
}, [usuario]);
const setValor = (e) => {
const nextUsuario = e.target.value;
setUsuario(nextUsuario);
// Option 2:
onUsuarioChange(nextUsuario);
};
return (
<EntradaDados
onChange={ setValor }
placeholder={ placeholder } />
);
}
export default PesqDados;
After studying properly, I found that I don't need to implement the function in the component page. I just needed to create a hook that calls the component's OnChange property on the component's page and then create a function just in the place where the component is installed. In this case, App.js.
Page Component....
const PesqDados = ({placeholder, Dados}) => {
return(
<EntradaDados
onChange={Dados}
placeholder={placeholder}
>
</EntradaDados>
);
}
export default PesqDados;
Page App.js
function App() {
const [usuario, SetUsuario] = useState('Aguardando Dados...')
const setValor =(e)=>{
SetUsuario(e.target.value);
}
const teste = ()=>{
alert("O usuário digitado foi : "+usuario)
};
return (
<>
<div className='divRepos'>
<div className='bloco'>
<div className='pesquisar'>
<p>{usuario}</p>
<PesqDados
placeholder={"Digite um username válido"}
Dados={setValor}
/>
<Button nomeBotao={"Pesquisar Perfil"}
onClick={teste}/>
</div>
...
Could someone tell me how to pass an array of disabled dates to the calendar?
I searched but couldn't find how to do it
https://www.npmjs.com/package/react-calendar
import React, { useState, useEffect} from 'react';
import Api from '../../services/api';
import Calendar from 'react-calendar';
import { useParams } from "react-router-dom";
import 'react-calendar/dist/Calendar.css';
function MyApp() {
const { id } = useParams();
const [value, onChange] = useState(new Date());
const [disabledDates, setDisabledDates] = useState([]);
useEffect(() => {
loadDisabledDates();
}, []);
function loadDisabledDates()
{
Api
.get("/dates/"+id)
.then((response) => {
setDisabledDates(response.data);
})
.catch((err) => {
console.error("error: " + err);
});
}
return (
<div>
<Calendar onChange={onChange} value={value} />
</div>
);
}
import React from "react"
import Calendar from 'react-calendar'
export default function Slots(){
const disableDates = new Date('August 19, 2022 23:15:30');
const date1=disableDates.getDate();
return(
<div className="calendar">
<Calendar
tileDisabled={({date}) => date.getDate()===date1}
/>
</div>
)
}
Edit:
Scrolling further down the documentation, there is also a prop called tileDisabled. So that's probably your answer.
First answer:
It looks, from the documentation, like your best bet is use the available props onClickDay, onClickMonth, etc
const handleClickDay = (e) => {
if(disableDates.includes(e.target.value)){
alert('this date is disabled')
}else{
//do something with the date
}
}
return(
<Calendar onChange={onChange} value={value}
onClickDay={handleClickDay} />
)
I haven't used this library, so I'm not sure e.target.value will give you exactly the data but it should be something like this.
I'm using recoiljs as my state manager for my react project, and one of my components doesn't call it's useEffect when a recoil atom changes from another file. Here is my main component that reads from an atom.
import React, {useState, useEffect} from 'react'
import '../css/MeadDeadline.css'
import {getNearestDate} from '../chromeAPI/retrieveDeadlineJSON'
import DeadlineList from '../atoms/deadlinelist'
import {useRecoilValue} from 'recoil'
export default function MainDeadline() {
// get the date and the stuff from chrome storage
const [school, setSchool] = useState("");
const [date, setDate] = useState("");
let [deadlinelist, setDeadlineList] = useRecoilValue(DeadlineList);
useEffect(() => {
const nearest = getNearestDate(deadlinelist);
const len = nearest.length;
if (len === 0){
setSchool("No schools registered");
setDate("");
} else if (len === 1){
setSchool(nearest[0].school);
setDate(nearest[0].date);
} else {
// we need to render a lot of stuff
console.log("error");
}
}, [deadlinelist]);
return (
<>
<div className="MainDeadline">
<div className='school'>{school}</div>
<div classNmae='date'>{date}</div>
</div>
</>
)
}
Here is my atom file
import {atom} from 'recoil'
const DeadlineList = atom({
key: "deadlinelist",
default: []
});
export default DeadlineList;
and here is the form that I'm submitting in
import React, {useState} from 'react'
import '../css/InputForm.css'
import checkList from '../utils/checkList'
import checkDate from '../utils/checkDate'
import {storeNewDeadline} from '../chromeAPI/storeNewDeadline'
import {useRecoilState} from 'recoil'
import DeadlineList from '../atoms/deadlinelist'
import SchoolList from '../atoms/schoollist'
export default function InputForm () {
const [inputschool, setInputSchool] = useState('');
const [inputdate, setInputDate] = useState('');
const [invalidschool, setInvalidSchool] = useState(false);
const [invaliddate, setInvalidDate] = useState(false);
const [badschool, setBadSchool] = useState('');
const [baddate, setBadDate] = useState('');
const [schoollist, setSchoolList] = useRecoilState(SchoolList);
const [deadlinelist, setDeadlineList] = useRecoilState(DeadlineList);
const validateForm = () => {
// check to make sure its not in the list
const valschool = checkList(schoollist, inputschool);
if (!valschool){
setInvalidSchool(true);
setBadSchool(inputschool);
} else {
setInvalidSchool(false);
setBadSchool("");
}
// check to make sure the date hasnt been reached yet
const valdate = checkDate(inputdate);
if (!valdate){ // add MSIN1DAY becauase the day value is 0 indexed so conflicts with Date() and date input
setInvalidDate(true);
setBadDate(inputdate);
}
else {
setInvalidDate(false);
setBadDate("");
}
return !invalidschool && !invaliddate; // want both to be valid
}
const handleSubmit = async(event) => {
event.preventDefault();
// validate the form
if (validateForm()){
storeNewDeadline(inputschool, inputdate);
// change schoollist state
let slist = schoollist;
slist.push(inputschool);
setSchoolList(slist);
// change deadlinelist state
let dlist = deadlinelist;
dlist.push({
"school": inputschool,
"date": inputdate
});
setDeadlineList(dlist);
console.log(deadlinelist, schoollist);
}
}
const handleChange = (event, fieldname) => {
switch (fieldname) {
case "inputschool":
setInputSchool(event.target.value);
break;
case "inputdate":
setInputDate(event.target.value);
break;
default:
break;
}
}
return (
<form className='InputForm' onSubmit={handleSubmit}>
<h3>Enter New School</h3>
<div id='inputname' className='Inputer'>
<p>School Name</p>
<input
type='text'
onChange={e => {handleChange(e, 'inputschool')}}
value={inputschool}
required
/>
{invalidschool ? <p>{badschool} is already registered</p> : null}
</div>
<div id='inputdate' className='Inputer'>
<p>Deadline Date</p>
<input
type='date'
onChange={e => {handleChange(e, 'inputdate')}}
value={inputdate}
required
/>
{invaliddate ? <p>{baddate} is invalid</p> : null}
</div>
<div id='inputsubmit' className='Inputer'>
<p>Submit</p>
<input type='submit' required></input>
</div>
</form>
)
}
If you want to just see file for file the github is here
The main component is src/components/MainDeadline.jsx , src/atoms/deadlinelist , src/components/InputForm.jsx
My main problem is when the user inputs something in the form, it's supposed to update the state, but the main component doesn't update.
Please tell me if I can improve my code in any way this is my first react project.
When dealing with arrays in state hooks, you need to clone the array as you do the set function.
Also, instead of this:
let [deadlinelist, setDeadlineList] = useRecoilValue(DeadlineList);
I would do this:
const [deadlinelist, setDeadlineList] = useRecoilState(DeadlineList);
I'm building a web-application with ReactJS and that needs me to implement a Search. So far, the search has been implemented (I'm using Fuse.js library for this) using a form with an input element. The form is implemented in the NavBar and after the user types a search-query, he is redirected to 'localhost:3000/search' URL where he can see the results corresponding to his query.
Here is the code I'm using for the form in the SearchBar.
import React, { useState } from 'react';
import { Form, FormControl } from 'react-bootstrap';
import { ReactComponent as SearchLogo } from '../../lib/search-logo.svg';
const SearchBar = () => {
const [searchQuery, setSearchQuery] = useState({ query: '' });
const searchQueryHandler = (event) => {
event.preventDefault();
setSearchQuery({ query: event.target.value });
};
const onFormSubmit = (event) => {
event.preventDefault();
window.location.href = "/search";
}
return (
<Form inline className="nav-search-form" onSubmit={onFormSubmit}>
<SearchLogo className="search-logo" />
<FormControl
type="text"
placeholder="Search spaces of interest"
className="nav-search"
value={searchQuery.query}
onChange={searchQueryHandler} />
</Form>
);
}
export default SearchBar;
I need to display the corresponding results in another SearchPage which will take the query from this component after submission and then display the results. Here is the code I have written for it.
import React, { useState, useRef } from 'react';
import { Col, Container, Row } from 'react-bootstrap';
import SpaceCardGrid from '../space-card-grid/space-card-grid';
import useSpaces from '../../utils/firebase/hooks/useSpaces';
import moment, { Moment } from 'moment';
import { roundTime } from '../../utils/date';
import Fuse from 'fuse.js';
const SearchPage = (queries) => {
const [date, setDate] = useState<[Moment, Moment]>([moment(new Date()), moment(new Date())]);
const [time, setTime] = useState([roundTime(), roundTime(30)]);
const [dateRangeType, setDateRangeType] = useState<'week' | 'day' | 'now'>('day');
const spaceCardGridRef = useRef(null);
const spaces = useSpaces(dateRangeType, date, time, 0);
const options = {
shouldSort: true,
keys: ['title', 'description'],
};
const fuse = new Fuse(spaces, options);
let filteredspaces = spaces;
if (queries.query !== '') {
const result = fuse.search(queries.query);
console.log(result);
filteredspaces = [];
result.forEach((space) => {
filteredspaces.push(space.item);
});
}
return (
<div>
<Container fluid className="bottom-container">
<Row style={{ justifyContent: 'center', alignItems: 'flex-start' }}>
<Col>
<div className="grid-root">
<SpaceCardGrid spaces={filteredspaces} spaceCardGridRef={spaceCardGridRef} />
</div>
</Col>
</Row>
</Container>
</div>
);
};
export default SearchPage;
Just for additional information useSpaces() is a function that gives me all the data (and it does so correctly), and filteredspaces is the final results array that I wish to display on the screen. All these things are perfectly working.
I'm stuck on how to pass the query between the two components though. The queries I have used in SearchPage(queries) is a dummy variable. I'm new to React, and I have learned about Redux, but it seems a lot of work (I might be wrong) for simply passing a value between 2 components. As you can clearly observe, the components aren't related but are independent. Is there a simple way to do this? Any help will be greatly appreciated!
you could use useContenxt along with useReducer hooks for a simpler state structure. I created a small example here. you can find more reference at docs
basically at root from your appplication you would start by creating a context and pass dispatch and query as values to your Provider:
export const QueryDispatch = React.createContext("");
const initialState = { query: "" };
export default function App() {
const [{ query }, dispatch] = useReducer(queryReducer, initialState);
return (
<QueryDispatch.Provider value={{ dispatch, query }}>
<SearchBar />
<SearchPage />
</QueryDispatch.Provider>
);
}
where queryReducer could be like:
export default function (state, action) {
switch (action.type) {
case 'update':
return {query: action.query};
default:
return state;
}
}
and at any component you could consume from your provider:
at your searchBar
import React, { useContext } from "react";
import { QueryDispatch } from "./App";
const SearchBar = () => {
const const { dispatch, query } = useContext(QueryDispatch);
const searchQueryHandler = (event) => {
dispatch({ type: "update", query: e.target.value })
};
..code
<FormControl
type="text"
placeholder="Search spaces of interest"
className="nav-search"
value={query}
onChange={searchQueryHandler} />
and at your SearchPage
import React, { useContext } from "react";
import { QueryDispatch } from "./App";
const SearchPage = () => {
const { query } = useContext(QueryDispatch);