The state values are passed in the searchYelp props as parameters in the handleSearch function. The values should be logged in the console with onClick. However, the argument passed into the term parameter is not logged into the console while the other two arguments (location, sortBy) are logged into the console.
import React, {useState} from 'react';
import './SearchBar.css';
const sortByOptions = {
'Best Match': 'best_match',
'Highest Rated': 'rating',
'Most Reviewed': 'review_count'
}
function SearchBar({ searchYelp }) {
const [ term, setTerm ] = useState('');
const [ location, setLocation ] = useState('');
const [ sortBy, setSortBy ] = useState('best_match');
const getSortByClass = (sortByOption) => {
if(sortBy === sortByOption) {
return 'active';
} else {
return '';
}
}
const handleSortByChange = (sortByOption) => {
setSortBy(sortByOption);
}
const handleTermChange = ({ target }) => {
setTerm(target.value);
}
const handleLocationChange = ({ target }) => {
setLocation(target.value)
}
const handleSearch = (e) => {
searchYelp(term, location, sortBy);
e.preventDefault();
}
const renderSortByOptions = () => {
return Object.keys(sortByOptions).map((sortByOption) => {
let sortByOptionValue = sortByOptions[sortByOption];
return <li onClick={()=>handleSortByChange(sortByOptionValue)} className={getSortByClass(sortByOptionValue)} key={sortByOptionValue}>{sortByOption}</li>
})
}
return(
<div className="SearchBar">
<div className="SearchBar-sort-options">
<ul>
{renderSortByOptions()}
</ul>
</div>
<div className="SearchBar-fields">
<input onchange={handleTermChange} placeholder="Search Businesses" />
<input onChange={handleLocationChange} placeholder="Where?" />
</div>
<div className="SearchBar-submit">
<a onClick={handleSearch} href="#">Let's Go</a>
</div>
</div>
)
}
export default SearchBar;
import React from 'react';
import './App.css';
import BusinessList from '../BusinessList/BusinessList';
import SearchBar from '../SearchBar/SearchBar';
const business = {
imageSrc: 'https://content.codecademy.com/programs/react/ravenous/pizza.jpg',
name: 'MarginOtto Pizzeria',
address: '1010 Paddington Way',
city: 'Flavortown',
state: 'NY',
zipCode: '10101',
category: 'Italian',
rating: 4.5,
reviewCount: 90
}
const businesses = [business, business, business, business, business, business]
function App() {
const searchYelp = (term, location, sortBy) => {
console.log(`you are searching ${term} ${location} ${sortBy}`)
}
return (
<div className="App">
<h1>ravenous</h1>
<SearchBar searchYelp={searchYelp} />
<BusinessList businesses={businesses}/>
</div>
);
}
export default App;
<input onchange={handleTermChange} placeholder="Search Businesses" />
onchange should be camel case, as onChange
Related
I'm trying to setup a piece of code into its own component, however, by doing so the data doesn't show up after doing it:
Code StackBlitz Example
As you see from the picture, both of the inputs from the component setup are blank. I'm assuming that it might have something to do with closures, but I'm not sure, and even more confused as how to resolve the issue.
App.tsx
import React, { useEffect, useState } from 'react';
import axios from 'axios';
import { SimpleTable } from './components/SimpleTable/SimpleTable';
import ITableCol from './components/SimpleTable/ITableCol';
interface IItem {
userId: number;
id: number;
title: string;
body: string;
}
interface IIdItem {
[key: string]: number;
}
export const App = () => {
const getItems = async () => {
const res = await axios.get('https://jsonplaceholder.typicode.com/posts');
const twoItems = [res.data[0], res.data[1]];
setItems(twoItems);
};
const getTableCols = (): ITableCol[] => {
return [
{
title: 'title',
renderFn: renderTitle,
},
{
title: 'input',
renderFn: renderInput,
},
];
};
const processData = () => {
let _idValues = { ...idValues };
for (const item of items) {
if (!_idValues[item.title]) {
_idValues[item.title] = item.id * 5;
}
}
return _idValues;
};
const renderTitle = (item: IItem) => {
return <div className="">{item.title}</div>;
};
const handleChange = (e: ChangeEvent<any>) => {};
const renderInput = (item: IItem) => {
const valueItem = idValues[item.title];
return (
<div className="">
<div>Id: {item.id}</div>
<div>
<input type="number" value={valueItem} onChange={handleChange} />
</div>
</div>
);
};
useEffect(() => {
getItems();
}, []);
const [items, setItems] = useState<IItem[]>([]);
const [idValues, setIdValues] = useState<IIdItem>({});
const [tableCols, setTableCols] = useState<ITableCol[]>([]);
useEffect(() => {
setTableCols(getTableCols());
setIdValues(processData());
}, [items]);
return (
<div className="p-2">
<h1>State Issue Example</h1>
<div className="mb-5">
<div>Simple Table</div>
<SimpleTable data={items} cols={tableCols} />
</div>
<div>
<div>Non Componentized Table</div>
<div className="d-flex">
<div className="w-50">
<b>title</b>
</div>
<div className="w-50">
<b>input</b>
</div>
</div>
</div>
<div>
{items.map((item) => {
return (
<div className="d-flex">
<div className="w-50">{renderTitle(item)}</div>
<div className="w-50">{renderInput(item)}</div>
</div>
);
})}
</div>
</div>
);
};
SimpleCode.tsx
import React, { useState } from 'react';
import ITableCol from '../SimpleTable/ITableCol';
type Props = {
cols: ITableCol[];
data: any[];
};
export const SimpleTable: React.FC<Props> = (props) => {
return (
<div>
<div className="d-flex">
{props.cols.map((col) => {
return (
<div className="w-50">
<b>{col.title}</b>
</div>
);
})}
</div>
{props.data.map((data, index) => {
return (
<div className="d-flex" key={'data-' + index}>
{props.cols.map((col, index) => {
return (
<div key={data.id} className="w-50">
{col.renderFn(data, index)}
</div>
);
})}
</div>
);
})}
<div>Data Size: {props.data.length}</div>
</div>
);
};
ITableCol.tsx
export default interface ITableCol {
renderFn: (item: any, index: number) => void;
title: string;
}
Problem is getTableCols gets render even before API response. Using two useEffect would have solved your issue.Screenshot of the Solution
useEffect(() => {
setIdValues(processData());
}, [items]);
useEffect(() => {
setTableCols(getTableCols());
}, [idValues]);
Given below is the full code of App.tsx
import React, { useEffect, useState } from 'react';
import axios from 'axios';
import { SimpleTable } from './components/SimpleTable/SimpleTable';
import ITableCol from './components/SimpleTable/ITableCol';
interface IItem {
userId: number;
id: number;
title: string;
body: string;
}
interface IIdItem {
[key: string]: number;
}
export const App = () => {
const getItems = async () => {
const res = await axios.get('https://jsonplaceholder.typicode.com/posts');
const twoItems = [res.data[0], res.data[1]];
setItems(twoItems);
};
const getTableCols = (): ITableCol[] => {
return [
{
title: 'title',
renderFn: renderTitle,
},
{
title: 'input',
renderFn: renderInput,
},
];
};
const processData = () => {
let _idValues = { ...idValues };
for (const item of items) {
if (!_idValues[item.title]) {
_idValues[item.title] = item.id * 5;
}
}
return _idValues;
};
const renderTitle = (item: IItem) => {
return <div className=""> {item.title}</div>;
};
const handleChange = (e: ChangeEvent<any>) => {};
const renderInput = (item: IItem) => {
const valueItem = idValues[item.title];
return (
<div className="">
<div>Id: {item.id}</div>
<div>valueItem: {valueItem}</div>
<div>
<input type="number" value={valueItem} onChange={handleChange} />
</div>
</div>
);
};
useEffect(() => {
getItems();
}, []);
const [items, setItems] = useState<IItem[]>([]);
const [idValues, setIdValues] = useState<IIdItem>({});
const [tableCols, setTableCols] = useState<ITableCol[]>([]);
useEffect(() => {
setIdValues(processData());
}, [items]);
useEffect(() => {
setTableCols(getTableCols());
}, [idValues]);
return (
<div className="p-2">
<h1>State Issue Example</h1>
<div className="mb-5">
<div>Simple Table</div>
<SimpleTable data={items} cols={tableCols} />
</div>
<div>
<div>Non Componentized Table</div>
<div className="d-flex">
<div className="w-50">
<b>title</b>
</div>
<div className="w-50">
<b>input</b>
</div>
</div>
</div>
<div>
{items.map((item, i) => {
return (
<div key={i} className="d-flex">
<div className="w-50">{renderTitle(item)}</div>
<div className="w-50">{renderInput(item)}</div>
</div>
);
})}
</div>
</div>
);
};
my code consists of a select by provinces, when I click the province to filter I get the result of clinics in that province, what I want is to get that result by clicking the button, not through the select itself.///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
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,
closeModal: () => void
}
export const SearchFilterClinics : React.FC<Props> = ({ children, isOpen, closeModal, }) => {
////filter
type OptionType = {
value: string;
label: string;
};
const provincesList: OptionType[] = [
{ value: 'Todos', label: 'Todos' },
{ value: 'Valencia', label: 'Valencia' },
{ value: 'Alicante', label: 'Alicante' },
{ value: 'Castellón', label: 'Castellón' },
]
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}
/>
<button onClick={handleChangeSelect}>buscar</button>
</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>
</>
)
}
It looks like you are calling the handleChangeSelect function with the button, and also when the select menu changes (with onChange). So remove the onChange in the select, and leave the onClick for the button.
<label>Provincia</label>
<Select
defaultValue={filterSelectClinic}
options={provincesList}
onChange={handleChangeSelect} //THIS TRIGGERS THE FUNCTION
/>
<button onClick={handleChangeSelect}>buscar</button> //ALSO TRIGGERS FUNCTION
Not sure where I am going wrong here. editMedicine should be a function but it is not. When I click the edit medicine button, I get an error onSubmit. I know in other areas of my code I had some misspellings and capitalization issues, I am hoping this is the case here . I have been looking at this for so long that I cant quite spot whats going on.
import React, { Fragment, useState, useContext, useEffect } from "react";
import { GlobalContext } from "../context/GlobalState.js";
import { useHistory, Link } from "react-router-dom";
const Editmedicine = (route) => {
let history = useHistory();
const { medicines, editMedicine } = useContext(GlobalContext);
const [selectedUser, setSeletedUser] = useState({
id: null,
name: "",
directions: "",
});
const currentUserId = route.match.params.id;
useEffect(() => {
const medicineId = currentUserId;
const selectedUser = medicines.find(
(med) => med.id === parseInt(medicineId)
);
setSeletedUser(selectedUser);
}, []);
const onSubmit = (e) => {
e.preventDefault();
editMedicine(selectedUser);
history.push("/");
};
const handleOnChange = (userKey, value) =>
setSeletedUser({ ...selectedUser, [userKey]: value });
if (!selectedUser || !selectedUser.id) {
return <div>sdf</div>;
}
return (
<Fragment>
<div id="editContainer">
<form onSubmit={onSubmit}>
<div className="w-full mb-5">
<label
htmlFor="name"
>
Name of medicine
</label>
<input
value={selectedUser.name}
onChange={(e) => handleOnChange("name", e.target.value)}
type="text"
placeholder="Enter name"
/>
</div>
<div className="w-full mb-5">
<label
htmlFor="directions"
>
Directions
</label>
<input
value={selectedUser.directions}
onChange={(e) => handleOnChange("directions", e.target.value)}
type="text"
placeholder="Enter directions"
/>
</div>
<div className="flex items-center justify-between">
<button id="editMedicine">
Edit medicine
</button>
</div>
<div className="text-center mt-4 text-gray-500">
<Link to="/">Cancel</Link>
</div>
</form>
</div>
</Fragment>
);
};
export default Editmedicine;
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.6.3/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.6.3/umd/react-dom.production.min.js"></script>
// here is the globalState file
import React, { createContext, useReducer } from "react";
import AppReducer from "./AppReducer";
//start by setting the initial state
const initialState = {
medicines: [
{
id: 1,
name: "Daily Multivitamin",
directions: "Take once a day with 2 glasses of water"
},
{
id: 2,
name: "Herbal capsule",
directions: "Take once a day with food"
},
{
id: 3,
name: "Tincture",
directions: "1 drop a day, 3x a day."
}
]
};
//create context
export const GlobalContext = createContext(initialState);
//provider component
export const GlobalProvider = ({ children }) => {
const [state, dispatch] = useReducer(AppReducer, initialState);
function removeMedicine(id) {
dispatch({
type: "REMOVE_MEDICINE",
payload: id
});
}
//this might have to be ADD_MEDICINE or S instead
function addMedicine(medicines) {
dispatch({
type: "ADD_MEDICINE",
payload: medicines
});
}
function editMedicine(medicines) {
dispatch({
type: "EDIT_MEDICINE",
payload: medicines
});
}
return (
<GlobalContext.Provider
value={{
medicines: state.medicines,
removeMedicine,
addMedicine,
editMedicine
}}
>
{children}
</GlobalContext.Provider>
);
};
I need to prevent the user from being able to add names that already exist in the person list but I'm not sure where should I add it and also what method is better to use .includes or indexOf? I want issue a warning with the alert command when such an action is attempted. Any help would be much appropriated!
import React, { useState } from 'react'
const App = () => {
const [ persons, setPersons ] = useState([ { name: 'Arto Hellas' }])
const [ newName, setNewName ] = useState('')
const addName = (event) => {
event.preventDefault()
const nameObject = {
name: newName,
}
setPersons([...persons,nameObject])
}
const handleNameChange = (event) => {
setNewName(event.target.value)
}
return (
<div>
<h2>Phonebook</h2>
<form onSubmit={addName} >
<div>
name: <input value={newName} onChange={handleNameChange} />
</div>
<div>
<button type="submit">add</button>
</div>
</form>
<h2>Numbers</h2>
{persons.map(person => (
<p key={person.name}>{person.name}</p>
))}
</div>
)
}
export default App
You need to do something in the following place:
const addName = (event) => {
event.preventDefault();
const nameObject = {
name: newName
};
setPersons([...persons, nameObject]);
};
Use .find() on the persons array to find the particular name already existing and add the condition before setPersons is executed.
if (persons.find(p => p.name === newName)) {
window.alert("Name already exists!");
return false;
}
A code like above will work.
import React, { useState } from "react";
const App = () => {
const [persons, setPersons] = useState([{ name: "Arto Hellas" }]);
const [newName, setNewName] = useState("");
const addName = (event) => {
event.preventDefault();
if (persons.find((p) => p.name === newName)) {
window.alert("Name already exists!");
return false;
}
const nameObject = {
name: newName
};
setPersons([...persons, nameObject]);
};
const handleNameChange = (event) => {
setNewName(event.target.value);
};
return (
<div>
<h2>Phonebook</h2>
<form onSubmit={addName}>
<div>
name: <input value={newName} onChange={handleNameChange} />
</div>
<div>
<button type="submit">add</button>
</div>
</form>
<h2>Numbers</h2>
{persons.map((person) => (
<p key={person.name}>{person.name}</p>
))}
</div>
);
};
export default App;
Demo: https://codesandbox.io/s/nervous-poitras-i3stw?file=/src/App.js:0-958
The above code shows an ugly Error Alert using the normal window alert. If you want a better error like this:
You can use the following code, by setting a state:
import React, { useState } from "react";
import "./styles.css";
const App = () => {
const [persons, setPersons] = useState([{ name: "Arto Hellas" }]);
const [newName, setNewName] = useState("");
const [error, setError] = useState(false);
const addName = (event) => {
event.preventDefault();
if (persons.find((p) => p.name === newName)) {
setError(true);
return false;
}
const nameObject = {
name: newName
};
setPersons([...persons, nameObject]);
};
const handleNameChange = (event) => {
setNewName(event.target.value);
};
return (
<div>
<h2>Phonebook</h2>
<form onSubmit={addName}>
{error && <p className="error">User already exists.</p>}
<div>
name: <input value={newName} onChange={handleNameChange} />
</div>
<div>
<button type="submit">add</button>
</div>
</form>
<h2>Numbers</h2>
{persons.map((person) => (
<p key={person.name}>{person.name}</p>
))}
</div>
);
};
export default App;
And here's our style.css:
.error {
background-color: red;
color: #fff;
padding: 5px;
}
Demo: https://codesandbox.io/s/vibrant-tree-wsou2?file=/src/App.js
First add field to check name exists or not:
const nameExists = React.useMemo(() => {
return persons.some(item => item.name === newName);
}, [newName, persons])
Then disable button and show message if name exists:
<div>
{nameExists && <p>Name {newName} already exists!</p>}
<button type="submit" disabled={nameExists} >add</button>
</div>
Also, make sure you clear name when you add new name:
const addName = (event) => {
...
setNewName('')
}
const App = () => {
const [ persons, setPersons ] = React.useState([ { name: 'Arto Hellas' }])
const [ newName, setNewName ] = React.useState('')
const addName = (event) => {
event.preventDefault()
const nameObject = {
name: newName,
}
setPersons([...persons,nameObject]);
setNewName('')
}
const handleNameChange = (event) => {
setNewName(event.target.value)
}
const nameExists = React.useMemo(() => {
return persons.some(item => item.name === newName);
}, [newName, persons])
return (
<div>
<h2>Phonebook</h2>
<form onSubmit={addName} >
<div>
name: <input value={newName} onChange={handleNameChange} />
</div>
<div>
{nameExists && <p>Name {newName} already exists!</p>}
<button type="submit" disabled={nameExists} >add</button>
</div>
</form>
<h2>Numbers</h2>
{persons.map(person => (
<p key={person.name}>{person.name}</p>
))}
</div>
)
}
ReactDOM.render(<App />, document.getElementById('root'))
<script crossorigin src="https://unpkg.com/react#17/umd/react.development.js"></script>
<script crossorigin src="https://unpkg.com/react-dom#17/umd/react-dom.development.js"></script>
<div id="root"></div>
You can use the find method to search for a person with the newName. Please check the below code-snippet:
const addName = (event) => {
event.preventDefault()
const nameObject = {
name: newName,
}
let personAlreadyExists = persons.find(person => person.name === newName);
if (personAlreadyExists) {
alert('Person with that name already exists');
// any other operations (like clearing user input in the form, etc..)
}
else {
setPersons([...persons, nameObject])
}
}
Code-Sandbox
I have two pages and two components LibraryPageFilters.tsx (url: /courses) and UserVideoCreatePage.tsx (url: /ugc/courses/${course.id}).
In component LibraryPageFilters.tsx
useEffect(() => {
console.log(course.id)
if (course.id) {
console.log(544)
dispatch(push(`/ugc/courses/${course.id}`));
}
}, [course]);
i have a check that if course.id present in the store, then we make a redirect.
In component UserVideoCreatePage.tsx
useEffect(() => {
return () => {
console.log(333344444)
dispatch(courseDelete());
};
}, []);
i am deleting a course from the store when componentUnmount.
why does unmount happen after a redirect? as a result, I am redirected back. Because the course is not removed from the store at the moment of unmount, and the check (if (course.id)) shows that the course is in the store and a redirect occurs back (dispatch(push(/ugc/courses/${course.id})))
UserVideoCreatePage.tsx
import React, { useEffect, useRef, useState } from 'react';
import { shallowEqual, useDispatch, useSelector } from 'react-redux';
import { useParams } from 'react-router';
import { Link } from 'react-router-dom';
import { Container } from 'Core/components/Container/Container';
import { Svg } from 'Core/components/Svg';
import { Button } from 'Core/Molecules/Button';
import { Select } from 'Core/Molecules/Select';
import {
agreementCourse, categoriesSelector,
courseDelete,
courseEditorCourseSelector,
courseUpdateApi, getCategories,
getCourse,
updateCourseApi,
} from 'Learnings/store/courseEdit';
import { CourseComments } from 'Learnings/screens/CoursePlayPage/CourseBottom/CourseComments/CourseComments';
import { AddItem } from './AddItem';
import s from 'Admin/Pages/Course/Description/index.scss';
import './UserVideoCreatePage.scss';
export const UserVideoCreatePage: React.FC = () => {
const dispatch = useDispatch();
const { id: idCourse } = useParams();
const course = useSelector(courseEditorCourseSelector, shallowEqual);
const categories = useSelector(categoriesSelector, shallowEqual);
const [value, valueSet] = useState({ name: '', description: '', categories: [], lectures: [], materials: [] });
const [tab, tabSet] = useState('program');
const inputFileRef = useRef<HTMLInputElement>(null);
const img = course.gallery_items && course.gallery_items[0];
console.log(categories);
const handleBtnClick = () => {
if (inputFileRef && inputFileRef.current) {
inputFileRef.current.click();
}
};
useEffect(() => {
dispatch(getCourse(idCourse));
dispatch(getCategories());
}, [idCourse]);
useEffect(() => {
valueSet({
name: course.name,
description: course.description,
categories: course.categories && course.categories[0] && course.categories[0].id,
lectures: course.lectures,
materials: course.materials,
});
}, [course]);
useEffect(() => {
return () => {
console.log(333344444)
dispatch(courseDelete());
};
}, []);
return (
<Container className="createCourse">
<Link to="/" className="gallery__back">
<Svg name="arrow_back" width={26} height={20} className="gallery__svg"/>
<span>Назад</span>
</Link>
<div className="createCourse__twoColumn">
<div className="createCourse__twoColumn-left">
<div className="inputBlock">
<label className="inputBlock__label" htmlFor="video">
Название видео-курса
</label>
<input
id="video"
type="text"
placeholder="Введите название вашего видео"
className="inputBlock__input"
value={value.name || ''}
onChange={e =>
valueSet({
...value,
name: e.target.value,
})
}
onBlur={e => {
if (e.target.value && course.name !== e.target.value) {
dispatch(updateCourseApi(idCourse, { name: e.target.value }));
}
}}
/>
</div>
<div className="inputBlock">
<label className="inputBlock__label" htmlFor="opisanie">
Описание видео-курса
</label>
<textarea
id="opisanie"
placeholder="Введите краткое описание вашего видео"
className="inputBlock__input"
value={value.description || ''}
onChange={e =>
valueSet({
...value,
description: e.target.value,
})
}
onBlur={e => {
if (e.target.value && course.description !== e.target.value) {
dispatch(updateCourseApi(idCourse, { description: e.target.value }));
}
}}
/>
</div>
<Select
title="Категории видео-курса"
placeholder="Категории видео-курса"
value={value.categories}
options={categories.map(category => ({ value: category.id, label: category.name }))}
onChange={val => {
valueSet({
...value,
categories: val,
});
dispatch(
updateCourseApi(idCourse, {
category_ids: val,
courses_curators: {
'': {
user_id: val,
},
},
}),
);
}}
search
/>
</div>
<div className="createCourse__twoColumn-right">
<div className="loadVideo">
<div className="loadVideo__field">
<div className="loadVideo__field--block">
{!img && (
<>
<Svg className="loadVideo__field--block-icon" name="icn-load" width={104} height={69}/>
<p className="loadVideo__field--block-text">Загрузите обложку к видео</p>
</>
)}
{img && <img src={img && img.image_url} alt=""/>}
</div>
</div>
<div className="loadVideo__under">
<div className="loadVideo__under--left">
<div className="loadVideo__under--text">
<span className="loadVideo__under--text-grey">*Рекомендуемый формат</span>
<span className="loadVideo__under--text-bold"> 356х100</span>
</div>
<div className="loadVideo__under--text">
<span className="loadVideo__under--text-grey">*Вес не должен превышать</span>
<span className="loadVideo__under--text-bold"> 10 Мб</span>
</div>
</div>
<div className="loadVideo__under--right">
<input
onChange={val => {
if (val.target.files[0]) {
if (img) {
dispatch(
updateCourseApi(idCourse, {
gallery_items: {
'': {
image: val.target.files[0],
id: img.id,
},
},
}),
);
} else {
dispatch(
updateCourseApi(idCourse, {
gallery_items: {
'': {
image: val.target.files[0],
},
},
}),
);
}
}
}}
type="file"
ref={inputFileRef}
className="Library__btn"
/>
<Button
onClick={() => {
handleBtnClick();
}}
>
Библиотека обложек
</Button>
</div>
</div>
</div>
</div>
</div>
<div className={`block-switcher block-switcher--courseCreate`}>
<div
className={`block-switcher__item ${tab === 'program' && 'block-switcher__item_active'}`}
onClick={() => tabSet('program')}
>
Программы
</div>
<div
className={`block-switcher__item ${tab === 'comments' && 'block-switcher__item_active'}`}
onClick={() => tabSet('comments')}
>
Комментарии эксперта
</div>
</div>
{tab === 'program' && (
<>
<AddItem
accept="video/mp4,video/x-m4v,video/*"
fieldName="name"
addType="lecture_type"
title="Видео-курсы"
addBtn="Добавить видео"
type="lectures"
file="video"
lecturesArg={course.lectures}
value={value}
onChangeInput={lecturesNew => {
valueSet({
...value,
lectures: lecturesNew,
});
}}
onVideoUpdate={(params: any) => {
dispatch(updateCourseApi(idCourse, params));
}}
posMove={(lectures: any) => {
dispatch(courseUpdateApi({ id: idCourse, lectures: lectures }, true));
}}
/>
<AddItem
accept=""
fieldName="title"
addType="material_type"
title="Материалы к видео-курсам"
addBtn="Добавить файл"
type="materials"
file="document"
lecturesArg={course.materials}
value={value}
onChangeInput={lecturesNew => {
valueSet({
...value,
materials: lecturesNew,
});
}}
onVideoUpdate={(params: any) => {
dispatch(updateCourseApi(idCourse, params));
}}
posMove={(lectures: any) => {
dispatch(courseUpdateApi({ id: idCourse, materials: lectures }, true));
}}
/>
</>
)}
{tab === 'comments' && <CourseComments title="Обсуждение"/>}
<Button
className={`${s.button} agreement__btn`}
size="big"
onClick={() =>
dispatch(
agreementCourse(idCourse, {
visibility_all_users: true,
}),
)
}
>
Отправить на согласование
</Button>
</Container>
);
};
LibraryPageFilters.tsx
import React, { useEffect, useState } from 'react';
import { useDispatch, useSelector, shallowEqual } from 'react-redux';
import { push } from 'connected-react-router';
import { getSettingsGlobalSelector } from 'Core/store/settings';
import { Svg } from 'Core/components/Svg';
import { NavBar } from 'Core/Organisms/NavBar';
import { Button } from 'Core/Molecules/Button';
import { CategoriesFilter } from 'Core/Organisms/Filters/components/CategoriesFilter';
import { courseDelete, courseEditorCourseSelector, createNewCourse } from 'Learnings/store/courseEdit';
import { FILTERS, LINKS } from '../../libraryPageConstants';
import { setLibraryPageQuery } from '../../actions/libraryPageActions';
import { getLibraryPageQuerySelector } from '../../libraryPageSelectors';
import s from './index.scss';
import { languageTranslateSelector } from 'Core/store/language';
import { LanguageType } from 'Core/models/LanguageSchema';
import { Status, Tabs } from 'Core/components/Tabs/Tabs';
const statuses: Array<Status> = [
{
key: 'Filter/all',
link: '/courses' || '/courses',
type: 'all' || '',
},
{
key: 'Filter/online',
link: '/courses/online',
type: 'online',
},
{
key: 'Filter/offline',
link: '/courses/offline',
type: 'offline',
},
{
key: 'Filter/complete',
link: '/courses/complete',
type: 'complete',
},
];
export const LibraryPageFilters = () => {
const dispatch = useDispatch();
const [searchTerm, setSearchTerm] = useState('');
const [isBtnDisabled, setIsBtnDisabled] = useState(false);
const course = useSelector(courseEditorCourseSelector, shallowEqual);
const global = useSelector(getSettingsGlobalSelector);
const query = useSelector(getLibraryPageQuerySelector, shallowEqual);
const courseCreateButtonText = useSelector(
languageTranslateSelector('CoursePage/courseCreateButton'),
) as LanguageType;
const { category_id: categoryID } = query;
console.log(course)
useEffect(() => {
console.log(course.id)
if (course.id) {
console.log(544)
dispatch(push(`/ugc/courses/${course.id}`));
}
}, [course]);
useEffect(() => {
return () => {
setIsBtnDisabled(false);
dispatch(courseDelete());
};
}, []);
const onFilter = (values: any) => {
return false;
};
const handleActiveCategory = (id: number) => {
const categoryParam = {
...query,
offset: 0,
category_id: id,
};
if (id === categoryID) {
delete categoryParam.category_id;
}
dispatch(setLibraryPageQuery(categoryParam));
};
const handleSearch = () => {
dispatch(setLibraryPageQuery({ query: searchTerm }));
};
return (
<React.Fragment>
<div className={s.filters}>
{global.coursesPage?.filters.length ? (
<NavBar
className={s.navBar}
links={global.coursesPage.filtersLinks.map(linkType => LINKS[linkType])}
filters={global.coursesPage.filters.map(filterType => FILTERS[filterType])}
onFilter={onFilter}
postfix={
global.coursesPage.courseCreateButton && global.coursesPage.courseCreateButton.enable ? (
<Button
className="coursePageCreateButton"
onClick={() => {
dispatch(createNewCourse());
setIsBtnDisabled(true);
}}
disabled={isBtnDisabled}
>
{courseCreateButtonText['CoursePage/courseCreateButton']}
</Button>
) : null
}
/>
) : (
<div className="track-page__header" data-tut="track-header">
<Tabs statuses={statuses} />
<div className={s.filtersSearch}>
<Svg className={s.filtersSearchIcon} name="search_alternative" width={18} height={18} />
<input
type="text"
placeholder="Поиск"
className={s.filtersSearchInput}
value={searchTerm}
onChange={event => setSearchTerm(event.target.value)}
/>
<button type="button" className={s.filtersButton} onClick={handleSearch}>
Найти
</button>
</div>
</div>
)}
</div>
<CategoriesFilter onChange={handleActiveCategory} selectedID={categoryID} />
</React.Fragment>
);
};
Although react suggests to use Functional Component, try Class Component, I faced similar issues, this was resolved easily in Class Component :
componentDidMount();
componentDidUpdate(prevProps, prevState, snapshot);
These two will solve your problem. Ask me if anything you need.