I'm using ant calendar which renders PanelBody(third party) component which has a prop call rowNum. Now I want to override this value to my custom value from my component:
import React from 'react';
import { Calendar } from 'antd';
import moment from 'moment';
import { Button } from '#material-ui/core';
import { getEventInfo, getQuest } from '../../../response/api';
import { EventInfo, Event } from '../../../response/type';
import { ConfigProvider } from 'antd';
import jaJP from 'antd/lib/locale/ja_JP';
interface State {
event_infos: EventInfo[];
events: Event[];
}
interface Props {
}
class EventCalendar extends React.Component<Props, State> {
constructor(props: Props) {
super(props);
this.state = {
event_infos: [],
events: []
};
}
componentDidMount() {
getEventInfo().then((item) => {
this.setState({event_infos: item});
});
getQuest().then((item) => {
this.setState({events: item});
});
}
getCorrectFormatDate = date => {
return moment(date, 'YYYY-MM-DD').format('YYYY-MM-DD');
}
getListEventInfoDate = value => {
let listData = [];
let listEventInfo = this.state.event_infos;
listEventInfo.map((item) => {
const startTime = this.getCorrectFormatDate(item.start_at);
const calendarTime = this.getCorrectFormatDate(value);
if (startTime === calendarTime) {
listData.push(item);
}
return listData;
});
return listData || [];
}
getListEventDate = value => {
let listData = [];
let listEvent = this.state.events;
listEvent.map((item) => {
const startTime = this.getCorrectFormatDate(item.start_at);
const calendarTime = this.getCorrectFormatDate(value);
if (startTime === calendarTime) {
listData.push(item);
}
return listData;
});
return listData || [];
}
dateCellRender = value =>{
const listEventInfos = this.getListEventInfoDate(value);
const listEvents = this.getListEventDate(value)
return (
<ul className="events-calendar">
{listEventInfos.map((item, i) => <img key={i} src={item.image} alt="event_info" />)}
{listEvents.map((item, i) => <Button key={i} variant="outlined" color="primary" className="button-event">{item.name}</Button>)}
</ul>
)
}
render() {
moment.locale("ja");
return(
<ConfigProvider locale={jaJP}>
<Calendar
dateCellRender={this.dateCellRender}
className="hide-header"
validRange={[moment('2020-12-13', 'YYYY-MM-DD'), moment('2021-01-16', 'YYYY-MM-DD')]}
defaultValue={moment('2020-12-13')}
/>
</ConfigProvider>
)
}
};
export default EventCalendar;
This component render Calendar which renders PanelBody. I tried to create a duplicated component then overrided the prop value, imported it to EventCalendar, but it cannot update the props value as expected. Is it my approach wrong? Any suggestion for this issue?
Related
I have an React Component Which Consists Dropdown , the dropdown has 4 values 'requsted,all,taken,available' and initially all the List of item are loaded. Each Item has isRequested,isTaken and isAvailable. Now, I need to filter those arrays accordingly to dropdown but i am getting something else as an output
My React Component is:
import React, { useContext, useEffect, SyntheticEvent, useState } from 'react'
import { observer } from 'mobx-react-lite';
import { Dropdown, Segment, Item, Icon, Label, Button, Select } from 'semantic-ui-react';
import { BooksStatus } from '../../app/common/options/BookStatus';
import { RootStoreContext } from '../../app/stores/rootStore';
import { format } from 'date-fns';
import { NavLink } from 'react-router-dom';
import { id } from 'date-fns/esm/locale';
const LibrarianManager: React.FC = () => {
const rootStore = useContext(RootStoreContext);
const { loadBooks, getAvailableBooks, deleteBook } = rootStore.bookStore;
const [status, setStatus] = useState(BooksStatus[0].value);
useEffect(() => {
loadBooks();
}, [loadBooks]);
const onChange = (value: any) => {
setStatus(value)
console.log(value)
if (value === 'requested') {
if (value === 'requested') {
getAvailableBooks.filter(data => data.isRequested == true)
console.log(getAvailableBooks)
}
}
}
return (
<div>
<Select
value={status}
onChange={(e, data) => onChange(data.value)}
options={BooksStatus}
/>
{getAvailableBooks.map(books => (
<Segment.Group key={books.bookName}>
<Segment>
<Item.Group>
<Item>
<Item.Image size='tiny' circular src='/assets/books.jpg' />
<Item.Content>
<Item.Header as={NavLink} to={`/booksDetail/${books.id}`} >{books.bookName}</Item.Header>
</Item.Content>
</Item>
</Item.Group>
</Segment>
<Segment clearing>
<span></span>
</Segment>
</Segment.Group>
))}
</div>
)
}
export default observer(LibrarianManager);
My Map Functions is :-
#computed get getAvailableBooks() {
return Array.from(this.bookRegistry.values());
}
#action loadBooks = async () => {
this.loadingInitial = true;
try {
const books = await agent.Books.list();
runInAction("loading books", () => {
books.forEach((books) => {
books.issuedOn = new Date(books.issuedOn);
this.bookRegistry.set(books.id, books);
});
this.loadingInitial = false;
});
} catch (error) {
runInAction("load books error", () => {
this.loadingInitial = false;
});
}
};
and my data Model or the item is
export interface IBooks {
id: number;
bookname: string;
issuedOn: Date;
returnDate: Date;
isReturned: boolean;
isRequested: boolean;
isAvailable: boolean;
isTaken: boolean;
name: string;
requestedBy: string;
}
while trying by this method
if (value === 'requested') {
if (value === 'requested') {
getAvailableBooks.filter(data => data.isRequested == true)
console.log(getAvailableBooks)
}
in console i am getting all the list without filtering
Array.filter doesn't mutate the original array but returns a new one which is nice (MDN).
What you want is:
const filteredBooks = getAvailableBooks.filter(data => data.isRequested == true)
console.log(filteredBooks)
I'm new to React and I'm trying to build a search filter fetching an API, the console doesn't give me any error, but the filter search bar doesn't work, could someone help me out? Thank you!
So, I think everything should be fine, because in the chrome console, I don't receive any errors, but the SearchBox.js, doesn't seem to work
This is the code:
SearchBox.js:
import React from 'react';
class SearchBox extends React.Component {
constructor(props) {
super(props)
this.state = {
suggestions: [],
text: '',
}
}
componentDidMount() {
fetch('https://api.scryfall.com/catalog/card-names')
.then(response => response.json())
.then(cards => this.setState({ suggestions: cards.data}))
}
onTextChanged = (event) => {
const { items } = this.props;
const value = event.target.value;
let suggestions = [];
if (value.length > 0) {
const regex = new RegExp(`^${value}`, '');
suggestions = Object.keys(items).sort().filter(word => regex.test(word))
}
this.setState(() => ({ suggestions, text: value }))
}
suggestionSelected (value) {
this.setState({
text: value,
suggestions: []
});
}
renderSuggestions () {
const { suggestions } = this.state;
if (suggestions.length === 0) {
return null;
}
return (
<ul>
{suggestions.map((item, index) => <li key={index} onClick={() => this.suggestionSelected(item)}>{item}</li>)}
</ul>
)
}
render () {
const { text } = this.state;
return (
<div>
<input value={text}
onChange={this.onTextChanged} type="text" />
{this.renderSuggestions()}
</div>
);
}
}
export default SearchBox;
App.js:
import React from 'react';
import SearchBox from './components/SearchBox';
class App extends React.Component{
render() {
return(
<div>
<SearchBox items/>
</div>
)
}
};
export default App;
You are missing the functions bindings:
class SearchBox extends React.Component {
constructor(props) {
super(props)
this.state = {
suggestions: [],
text: '',
}
// == Binding ==
this.suggestionSelected = this.suggestionSelected.bind(this);
this.renderSuggestions = this.renderSuggestions.bind(this);
this.onTextChanged = this.onTextChanged.bind(this);
// == ======= ==
}
// [...]
}
I am facing such problem, i got my array of records fetched from an API, mapped it into single elements and outputting them as single components. I have function which changes state of parent Component, passes value to child component and child component should hide/show div content after button is clicked.
Of course. It is working, but partially - my all divs are being hidden/shown. I have set specific key to each child component but it doesn't work.
App.js
import React, { Component } from 'react';
import './App.css';
import axios from 'axios';
import countries from '../../countriesList';
import CitySearchForm from './CitySearchForm/CitySearchForm';
import CityOutput from './CityOutput/CityOutput';
import ErrorMessage from './ErrorMessage/ErrorMessage';
class App extends Component {
state = {
country: '',
error: false,
cities: [],
infoMessage: '',
visible: false
}
getCities = (e) => {
e.preventDefault();
const countryName = e.target.elements.country.value.charAt(0).toUpperCase() + e.target.elements.country.value.slice(1);
const countryUrl = 'https://api.openaq.org/v1/countries';
const wikiUrl ='https://en.wikipedia.org/w/api.php?action=query&prop=extracts&exintro&explaintext&format=json&category=city&redirects&origin=*&titles=';
const allowedCountries = new RegExp(/spain|germany|poland|france/, 'i');
if (allowedCountries.test(countryName)) {
axios
.get(countryUrl)
.then( response => {
const country = response.data.results.find(el => el.name === countryName);
return axios.get(`https://api.openaq.org/v1/cities?country=${country.code}&order_by=count&sort=desc&limit=10`)
})
.then( response => {
const cities = response.data.results.map(record => {
return { name: record.city };
});
cities.forEach(city => {
axios
.get(wikiUrl + city.name)
.then( response => {
let id;
for (let key in response.data.query.pages) {
id = key;
}
const description = response.data.query.pages[id].extract;
this.setState(prevState => ({
cities: [...prevState.cities, {city: `${city.name}`, description}],
infoMessage: prevState.infoMessage = ''
}))
})
})
})
.catch(error => {
console.log('oopsie, something went wrong', error)
})
} else {
this.setState(prevState => ({
infoMessage: prevState.infoMessage = 'This is demo version of our application and is working only for Spain, Poland, Germany and France',
cities: [...prevState.cities = []]
}))
}
}
descriptionTogglerHandler = () => {
this.setState((prevState) => {
return { visible: !prevState.visible};
});
};
render () {
return (
<div className="App">
<ErrorMessage error={this.state.infoMessage}/>
<div className="form-wrapper">
<CitySearchForm getCities={this.getCities} getInformation={this.getInformation} countries={countries}/>
</div>
{this.state.cities.map(({ city, description }) => (
<CityOutput
key={city}
city={city}
description={description}
show={this.state.visible}
descriptionToggler={this.descriptionTogglerHandler} />
))}
</div>
);
}
}
export default App;
CityOutput.js
import React, { Component } from 'react';
import './CityOutput.css';
class CityOutput extends Component {
render() {
const { city, descriptionToggler, description, show } = this.props;
let descriptionClasses = 'output-record description'
if (show) {
descriptionClasses = 'output-record description open';
}
return (
<div className="output">
<div className="output-record"><b>City:</b> {city}</div>
<button onClick={descriptionToggler}>Read more</button>
<div className={descriptionClasses}>{description}</div>
</div>
)
}
};
export default CityOutput;
Put the visible key and the toggle function in the CityOutput instead of having it in the parent
import React, { Component } from "react";
import "./CityOutput.css";
class CityOutput extends Component {
state = {
visible: true
};
descriptionTogglerHandler = () => {
this.setState({ visible: !this.state.visible });
};
render() {
const { city, description } = this.props;
let descriptionClasses = "output-record description";
if (this.state.visible) {
descriptionClasses = "output-record description open";
}
return (
<div className="output">
<div className="output-record">
<b>City:</b> {city}
</div>
<button onClick={() => this.descriptionTogglerHandler()}>Read more</button>
<div className={descriptionClasses}>{description}</div>
</div>
);
}
}
export default CityOutput;
There are two ways of how I would approach this,
The first one is setting in your state a key property and check and compare that key with the child keys like:
state = {
country: '',
error: false,
cities: [],
infoMessage: '',
visible: false.
currKey: 0
}
descriptionTogglerHandler = (key) => {
this.setState((prevState) => {
return { currKey: key, visible: !prevState.visible};
});
};
// then in your child component
class CityOutput extends Component {
render() {
const { city, descriptionToggler, description, show, currKey, elKey } = this.props;
let descriptionClasses = 'output-record description'
if (show && elKey === currKey) {
descriptionClasses = 'output-record description open';
}
return (
<div className="output">
<div className="output-record"><b>City:</b> {city}</div>
<button onClick={() => descriptionToggler(elKey)}>Read more</button>
<div className={descriptionClasses}>{description}</div>
</div>
)
}
};
The other way is to set an isolated state for every child component
class CityOutput extends Component {
constructor(props) {
this.state = {
show: false
}
}
function descriptionToggler() {
const {show} = this.state;
this.setState({
show: !show
})
}
render() {
const { city, descriptionToggler, description } = this.props;
let descriptionClasses = 'output-record description'
if (this.state.show) {
descriptionClasses = 'output-record description open';
}
return (
<div className="output">
<div className="output-record"><b>City:</b> {city}</div>
<button onClick={descriptionToggler}>Read more</button>
<div className={descriptionClasses}>{description}</div>
</div>
)
}
};
I hope this helps ;)
I am creating a basic todo application in react with flux. I am trying to make it so that every time a new todo is entered in the API, the list is update in real time rather than me having to reload the page the and application making another request. I have tried using componentDidUpdate; however that doesn't seem to work in this case. How should I go about implementing that?
My Todos.js
import React from "react";
import * as TodoActions from "../actions/TodoActions";
import TodoStore from "../stores/TodoStore";
import './Todos.css'
export default class Todos extends React.Component {
constructor() {
super();
this.addItem = this.addItem.bind(this);
this.deleteTodo = this.deleteTodo.bind(this)
this.getTodos = this.getTodos.bind(this);
this.state = {
todos: TodoStore.getAll(),
};
TodoActions.receiveTodos()
}
componentWillMount() {
TodoStore.addChangeListener(this.getTodos);
}
componentWillUnmount() {
TodoStore.removeChangeListener(this.getTodos);
}
componentDidMount() {
TodoActions.receiveTodos();
}
componentDidUpdate() {
TodoActions.receiveTodos();
}
getTodos() {
this.setState({
todos: TodoStore.getAll(),
});
}
deleteTodo(id) {
TodoActions.deleteTodo(id);
}
addItem(e) {
e.preventDefault();
TodoActions.createTodo(this._inputElement.value)
}
render() {
const { todos } = this.state;
const TodoComponents = todos.map((todo) => {
return (
<div key={todo.id} className="todo-list">
<div className="todo-name">{todo.name}</div>
<div className="todo-btn"><button type="button" onClick={() => this.deleteTodo(todo.id)}>Delete</button></div>
</div>
)
});
return (
<div className="main-container">
<h1 className="title">Todos</h1>
<ul>{TodoComponents}</ul>
<form onSubmit={this.addItem}>
<input ref={(a) => this._inputElement = a} placeholder="Enter Task" className="input-form"/>
<button type="submit" className="input-btn">Add</button>
</form>
</div>
);
}
}
My TodoStore.js
import { EventEmitter } from "events";
import AppDispatcher from "../dispatcher";
let _todos = []
function setTodos(todos) {
_todos = todos;
}
class TodoStore extends EventEmitter {
emitChange = () => {
this.emit("change");
}
addChangeListener = (callback) => {
this.on("change", callback)
}
removeChangeListener = (callback) => {
this.removeListener("change", callback)
}
getAll = () => {
return _todos;
}
handleActions = (action) => {
switch(action.type) {
case "RECEIVE_TODOS": {
setTodos(action.todos);
this.emit("change")
break;
}
}
}
}
const todoStore = new TodoStore;
AppDispatcher.register(todoStore.handleActions.bind(todoStore));
export default todoStore;
My TodoActions.js
import AppDispatcher from "../dispatcher";
import apiClient from "../api-client"
export function createTodo(name) {
apiClient.createTodo({name: name}).then((result) => {
AppDispatcher.dispatch({
type: "CREATE_TODO",
name: result,
});
})
}
export function deleteTodo(id) {
apiClient.deleteTodo(id).then((result) => {
AppDispatcher.dispatch({
type: "DELETE_TODO",
id,
});
})
}
export function receiveTodos() {
apiClient.getAllTodos().then(result => {
AppDispatcher.dispatch({
type: "RECEIVE_TODOS",
todos: result.payload
});
})
}
I've created a simple todo list to learn react and i'm trying to add some additional features. At the moment i'm trying to add buttons that toggle the list of items, so it either shows all the tasks or just those that are completed.
I've written a function to change the state of my visabilityFilter so I can later use this to toggle the items in the list, but it isn't behaving how it should be.
I console log the visabilityFilter variable but it always shows the wrong state before changing to the correct state. e.g. the 'show all' button will console log 'show completed' then if you press it again it will console log 'show all'
App.js
import React, { Component } from 'react';
import './App.css';
import TodoList from './components/TodoList.js'
import VisabilityFilter from './components/VisabilityFilter.js'
export const SHOW_ALL = 'show_all'
export const SHOW_COMPLETED = 'show_completed'
class App extends Component {
constructor (props) {
super(props)
this.state = {
inputValues: {
'newTodo': ''
},
todos: [
{
task: 'My First Todo',
completed: false
}
],
visabilityFilter: SHOW_ALL
}
this.addTodo = this.addTodo.bind(this)
this.handleInputChange = this.handleInputChange.bind(this)
this.handleKeyUp = this.handleKeyUp.bind(this)
this.toggleCompleted = this.toggleCompleted.bind(this)
this.removeTodo = this.removeTodo.bind(this)
this.checkCompleted = this.checkCompleted.bind(this)
this.setVisabilityFilter = this.setVisabilityFilter.bind(this)
}
handleInputChange (e) {
const { inputValues } = this.state
const { id, value } = e.target
this.setState({
inputValues: {
...inputValues,
[id]: value
}
})
}
handleKeyUp (e) {
var code = e.key
if(code === 'Enter') {
this.addTodo(e);
}
}
toggleCompleted (e, index) {
const { todos } = this.state
todos[index].completed = !todos[index].completed
todos.sort((a, b) => b.completed - a.completed)
this.setState({ todos })
}
removeTodo (e, index) {
const { todos } = this.state
this.setState ({ todos: todos.filter((todo, i) => i !== index) })
}
addTodo (e) {
const { todos, inputValues } = this.state
const { dataset } = e.target
if (inputValues[dataset.for] === '') return
const newTodo = { task: inputValues[dataset.for], completed: false }
todos.push(newTodo)
this.setState({
todos,
inputValues: { ...inputValues, [dataset.for]: '' }
})
}
checkCompleted (e, index) {
const { todos } = this.state
return { todos } && todos[index].completed
}
setVisabilityFilter (e) {
const { visabilityFilter } = this.state
const { dataset } = e.target
this.setState({
visabilityFilter: dataset.for
})
console.log ({ visabilityFilter })
}
render() {
const { todos, inputValues, visabilityFilter } = this.state
return (
<div className="App">
<TodoList
todos={todos}
inputValues={inputValues}
addTodo={this.addTodo}
handleInputChange={this.handleInputChange}
removeTodo={this.removeTodo}
toggleCompleted={this.toggleCompleted}
handleKeyUp={this.handleKeyUp}
checkCompleted={this.checkCompleted}
/>
<VisabilityFilter setVisabilityFilter={this.setVisabilityFilter} />
</div>
);
}
}
export default App;
VisabilityFilter.js
import React from 'react'
import { func } from 'prop-types'
import { SHOW_ALL, SHOW_COMPLETED } from '../App'
const VisabilityFilter = props => {
return (
<div>
<button data-for={SHOW_COMPLETED} onClick={ props.setVisabilityFilter } >
Show Completed Tasks
</button>
<button data-for={SHOW_ALL} onClick={ props.setVisabilityFilter }>
Show All Tasks
</button>
</div>
)
}
VisabilityFilter.propTypes = {
setVisabilityFilter: func.isRequired
}
export default VisabilityFilter
setState() is async (React docs), so the state changes won't be applied immediately. If you want to log out the new state,setState() takes in a function as the second argument and performs that function when the state is updated. So:
this.setState({
abc: xyz
},
() => console.log(this.state.abc),
)
Or you can also use componentDidUpdate(), which is recommended
In the functional components, you can use useEffect to track changes in state.
useEffect(() => {
console.log(someState);
},[someState);