Making a toggle button in react-admin in Show - reactjs

In react-admin, I want to create a toggle button which on the basis of default value from database will allow user to change the state and make corresponding changes both in the Show as well as in backend database.
Currently My code looks like this:
default class Deploy extends React.Component<{ data: any }> {
handleClick = () => {
const status = this.props.data.status;
alert(status);
};
render() {
const status = this.props.data.status;
return (
<Fragment>
<Button color="primary" onClick={this.handleClick}>
<ActionEject />
{status === "DEPLOYED" ? "UNDEPLOY" : "DEPLOY"}
</Button>
</Fragment>
);
}
}
class Actions extends React.Component<{ basePath: any; data: any; resource: any }, any> {
render() {
const basePath = this.props.basePath;
const data = this.props.data;
const resource = this.props.resource;
if (this.props.data) {
const defaultValue = this.props.data.default;
return (
<Deploy data={data} />
);
}
return null;
}
}
export default class ModelShow extends React.Component {
render() {
return (
<Show title={<ModelName />} actions={<Action />} {...this.props}>
<TabbedShowLayout>
<Tab label="Summary">
<TextField source="id" />
<TextField source="status" />
</Tab>
</TabbedShowLayout>
</Show>
);
}
}
P.S: I'm using Typescript.

You'll find multiple examples in the documentation for Actions
In order to correctly update the react-admin state, you should follow either the Using a Data Provider Instead of Fetch example or the Using a Custom Action Creator example.
Here's an example using the dataProvider directly:
// Import the UPDATE verb
import { UPDATE } from 'react-admin';
// Import your dataProvider
import dataProvider from '../dataProvider';
default class Deploy extends React.Component<{ data: any }> {
handleClick = () => {
const status = this.props.data.status;
const { push, record, showNotification } = this.props;
const updatedRecord = { ...record, status };
dataProvider(UPDATE, 'you_resource_name', { id: record.id, data: updatedRecord })
.then(() => {
// Optional notification
showNotification('Resource deployed');
// Optional redirection to the list page
push('/you_resource_name');
})
.catch((e) => {
console.error(e);
showNotification('Error: resource not deployed', 'warning')
});
};
render() {
const status = this.props.data.status;
return (
<Fragment>
<Button color="primary" onClick={this.handleClick}>
<ActionEject />
{status === "DEPLOYED" ? "UNDEPLOY" : "DEPLOY"}
</Button>
</Fragment>
);
}
}

Related

How to access to record properties at drawer level with React-Admin?

I use Edit forms in a drawer from a List page and I tried without success to find a way to access to the record or filter properties from there. I need to replace match.params.id with a coupound id ({id : { id : 1, category : 2}}) because my Tag ressource is a stored procedure with category as input parameter
Do you have any idea ?
Sample code:
import { connect } from 'react-redux';
import compose from 'recompose/compose';
import { push } from 'react-router-redux';
import { withStyles } from '#material-ui/core';
import { Datagrid, List, TextField, EditButton, Filter, TextInput } from 'react-admin';
import React from 'react';
import { Route } from 'react-router';
import { Drawer } from '#material-ui/core';
import TagEdit from './TagEdit';
const styles = {
drawerContent: {
width: 300
}
};
const TagFilters = (props) => (
<Filter {...props} variant="standard">
<TextInput label="Category" source="category" alwaysOn />
<TextInput label="Name" source="name" alwaysOn />
</Filter>
);
class TagList extends React.Component {
render() {
const { push, classes, ...props } = this.props;
return (
<Fragment>
<List
{...props}
sort={{ field: 'name', order: 'ASC' }}
filters={<TagFilters />}
>
<Datagrid>
<TextField source="category" />
<TextField source="name" />
<EditButton />
</Datagrid>
</List>
<Route path="/tags/:id">
{({ match }) => {
const isMatch =
match &&
match.params &&
match.params.id !== 'create';
return (
<Drawer
open={isMatch}
anchor="right"
onClose={this.handleClose}
>
{isMatch ? (
<TagEdit
className={classes.drawerContent}
id={isMatch ? match.params.id : null} // <------------- here I'd like to access to row record object or filter value ----------------------
onCancel={this.handleClose}
{...props}
/>
) : (
<div className={classes.drawerContent} />
)}
</Drawer>
);
}}
</Route>
</Fragment>
);
}
handleClose = () => {
this.props.push('/tags');
};
}
export default compose(
connect(undefined, { push }),
withStyles(styles)
)(TagList);```
OK, I reply myself, it was quite easy in fact :
const getFilterValues = (state, resource) => {
let values = undefined;
if (!state || !resource) return values;
let filterJSON =
state.router &&
state.router.location &&
state.router.location.query &&
state.router.location.query.filter;
if (filterJSON) {
let filters = JSON.parse(decodeURIComponent(filterJSON));
if (filters) {
values = filters;
}
}
else {
filterJSON =
state.admin &&
state.admin.resources &&
state.admin.resources[resource] &&
state.admin.resources[resource].list &&
state.admin.resources[resource].list.params &&
state.admin.resources[resource].list.params.filter;
if (filterJSON) {
values = filterJSON;
}
}
return values;
};
const mapStateToProps = (state, props) => {
let filters = getFilterValues(state, 'tags');
return {
...props,
filters
};
};
const CustomTagList = connect(
mapStateToProps,
)(TagList);
Don't forget to change last lines of code :
export default compose(
connect(undefined, { push }),
withStyles(styles)
)(CustomTagList);```
Now you have props.filters value in TagEdit.
:)

Change Authorization for Users on React/Firebase

Using React and Firebase I have a small page where users can vote on polls.
I am trying to achieve that all polls (saved on Firebase Database) can be seen by all users (registered via Firebase Authentication).
At the moment only users that are manually assigned to the poll in the database can see the poll on their Dashboard.
Below is the Dashboard.js file. Can anyone help me to figure out what I have to change to authorize all users to see all polls automatically?
import React from 'react';
import { Link } from 'react-router';
import { firebaseApp } from '../utils/firebase';
import Helmet from "react-helmet";
import FlatButton from 'material-ui/FlatButton';
import IconButton from 'material-ui/IconButton';
import Dialog from 'material-ui/Dialog';
import Paper from 'material-ui/Paper';
import Divider from 'material-ui/Divider';
import Loading from './Loading';
class Dashboard extends React.Component {
constructor(props) {
super(props);
this.state = {
dialogOpen: false,
loading: true,
polls: [] //items like { id: 34324, title: 'sdf'}
};
this.poll2Delete = '';
this.poll2DeleteTitle = ''
this.handleClose = this.handleClose.bind(this);
this.handleDelete = this.handleDelete.bind(this);
}
componentWillMount() {
//const uid = getLocalUserId();
firebaseApp.auth().onAuthStateChanged(user => {
if (user) { //this can get called after componentWillUnmount, make sure its there to avoid errors
const uid = user.uid;
this.userPollsRef = firebaseApp.database().ref(`user-polls/${uid}`);
//check if user has no polls to quit loading indicator
this.userPollsRef.once('value').then(snapshot => {
if (!snapshot.hasChildren()) {
if (this.mounted) {
this.setState({ loading: false });
}
}
});
this.userPollsRef.on('child_added', ((newPollIdSnapshot) => {
const pollId = newPollIdSnapshot.key;
firebaseApp.database().ref(`polls/${pollId}/title`).once('value').then(snapshot => {
const title = snapshot.val();
const polls = this.state.polls;
polls.push({ title: title, id: pollId })
if (this.mounted) {
this.setState({
polls: polls,
loading: false
});
}
});
})).bind(this);
this.userPollsRef.on('child_removed', ((removedPollIdSnapshot) => {
const pollId = removedPollIdSnapshot.key;
const polls = this.state.polls.filter(poll => poll.id !== pollId);
if (this.mounted) {
this.setState({
polls: polls
});
}
})).bind(this);
}
});
this.mounted = true; //the callbacks above can be called after componentWillUnmount(), to avoid errors, check
}
componentWillUnmount() {
this.userPollsRef.off();
this.mounted = false;
}
handleOpen(pollId) {
this.setState({ dialogOpen: true });
this.poll2Delete = pollId;
this.poll2DeleteTitle = this.state.polls.find(poll => poll.id === this.poll2Delete).title;
}
handleClose() {
this.setState({ dialogOpen: false });
}
handleDelete() {
// updating to null deletes
const updates = {};
updates[`/polls/${this.poll2Delete}`] = null;
updates[`/user-polls/${firebaseApp.auth().currentUser.uid}/${this.poll2Delete}`] = null;
firebaseApp.database().ref().update(updates);
this.setState({ dialogOpen: false });
}
render() {
const actions = [
<FlatButton
label="Cancel"
primary={false}
onTouchTap={this.handleClose}
/>,
<FlatButton
label="Delete"
primary={true}
onTouchTap={this.handleDelete}
/>,
];
let pollsUIs = this.state.polls.map((poll) => {
return (
<div key={poll.id} >
<IconButton
iconClassName="fa fa-trash"
/>
<Link to={`/polls/poll/${poll.id}`}>
<FlatButton
label={poll.title}
style={{ textAlign: 'left', width: '50%' }}
/>
</Link>
<Divider />
</div>
);
});
return (
<div className="row">
<div className="col-sm-12 text-xs-center">
<Helmet title="Dashboard" />
<Paper>
<br />
<h2> Current Polls </h2>
<br />
<Dialog
actions={actions}
modal={false}
open={this.state.dialogOpen}
onRequestClose={this.handleClose}
>
Delete "{this.poll2DeleteTitle}"?
</Dialog>
<br /><br />
{pollsUIs}
<Loading loading={this.state.loading} />
<br /><br />
</Paper>
</div>
</div>
);
}
}
export default Dashboard;
Not used to firebase, but and I think you have to remove uid from the paramater in order to get all the results from firebase.
this.userPollsRef = firebaseApp.database().ref(`user-polls/${uid}`)
to like this
this.userPollsRef = firebaseApp.database().ref(`user-polls`)

How to pass a Redux action into 2 reducers to update state in React?

My app has a clickable item in CurrentStatus component that passes a service.id to the parent component Dashboard and gets service_notes via a Redux action with axios.get. service_notes are passed into a reducer and into the Redux store. I then connect to the store in ServiceLogs component and iterate through the array to display in render() on the DOM. ServiceLogs is a comments type component where a user can add notes. I'm able to create the notes but can't update the state. My latest approach was taking the CREATE_NOTE action and using it in notesReducer AND serviceNotesReducer. This still doesn't update state and the DOM.
Here is my layout:
Here are the relevant components:
Dashboard:
import React, { Component } from "react";
import { connect } from "react-redux";
import { Container, Grid, Button } from "semantic-ui-react";
import CurrentStatus from "./components/CurrentStatusComponent";
import KnownOutages from "./components/KnownOutagesComponent";
import ServiceLogs from "./components/ServiceLogsComponent";
import { getServices } from "./actions/getServicesAction";
import { getServiceNotes } from "./actions/getServiceNotesAction";
import { getOutages } from "./actions/getOutagesAction";
class Dashboard extends Component {
state = {
serviceNotes: null,
serviceOutages: null,
showServiceLogs: "none",
}
componentDidMount() {
this.props.getServices();
this.props.getOutages();
}
displayServiceLogs = serviceId => {
debugger
this.props.getServiceNotes(serviceId)
this.setState({ showServiceLogs: "none" ? "block" : "none"});
}
render() {
console.log(this.state)
return (
<>
<Container fluid>
<h1>TML Dashboard</h1>
</Container>
<Grid columns={3} divided>
<Grid.Row>
<Grid.Column width={5}>Service Log</Grid.Column>
<Grid.Column width={6}>Current Status</Grid.Column>
<Grid.Column width={3}>Known Outages</Grid.Column>
</Grid.Row>
<Grid.Row>
<Grid.Column width={5}>
<ServiceLogs showServiceLogs={this.state.showServiceLogs}/>
</Grid.Column>
<Grid.Column width={6}>
<CurrentStatus displayServiceLogs={this.displayServiceLogs}/>
</Grid.Column>
<Grid.Column width={3}>
<KnownOutages />
</Grid.Column>
</Grid.Row>
</Grid>
</>
);
}
}
const mapStateToProps = state => {
return {
services: state.services.services,
notes: state.notes.notes
}
}
const mapDispatchToProps = dispatch => {
return {
getServices: () => dispatch(getServices()),
getNotes: () => dispatch(getNotes()),
getOutages: () => dispatch(getOutages()),
getServiceNotes: serviceId => dispatch(getServiceNotes(serviceId))
}
}
export default connect(mapStateToProps, mapDispatchToProps)(Dashboard);
Here is the CurrentStatus component where I click on an item (service) and pass the id up to Dashboard to get from in getServiceNotes(serviceId) function in getServiceNotesActoin:
import React, { Component } from "react";
import { connect } from "react-redux";
import { Table, Header } from "semantic-ui-react";
const uuidv4 = require("uuid/v4")
class CurrentStatus extends Component {
handleClick = serviceId => {
this.props.displayServiceLogs(serviceId)
}
render() {
console.log(Object.keys(this.props.services))
return (
<>
<Table celled padded>
<Table.Header>
<Table.Row>
<Table.HeaderCell singleLine>Service</Table.HeaderCell>
<Table.HeaderCell>Status</Table.HeaderCell>
<Table.HeaderCell>Reason</Table.HeaderCell>
</Table.Row>
</Table.Header>
<Table.Body>
{Object.assign(this.props.services).map((service) => (
<Table.Row key={uuidv4()}>
<Table.Cell
onClick={() => this.handleClick(service.id)}
>
<Header as="h3" textAlign="center">
{service.name}
</Header>
</Table.Cell>
<Table.Cell textAlign="center">
{service.is_down ? (
<h4 style={{ color: "red" }}>Down</h4>
) : (
<h4 style={{ color: "green" }}>Up</h4>
)}
</Table.Cell>
<Table.Cell></Table.Cell>
</Table.Row>
))}
</Table.Body>
</Table>
</>
);
}
};
const mapStateToProps = state => {
return {
services: state.services.services
}
};
export default connect(mapStateToProps, null)(CurrentStatus);
and here is the ServiceLogs component where I'm able to display and show the related serviceNotes:
import React, { Component } from "react";
import { connect } from "react-redux";
import { Comment, Container, Grid, Form, Button } from "semantic-ui-react";
import { createNote } from "../actions/createNoteAction";
class ServiceLogsComponent extends Component {
state = {
entry: ""
}
handleChange = (e, { name, value }) => this.setState({ [name]: value })
handleSubmit = e => {
e.preventDefault()
const userId = 2
const serviceId = this.props.serviceNotes[0].service.id
this.props.createNote(this.state.entry, serviceId, userId)
}
render() {
console.log(this.props)
return (
<>
<div style={{ display: this.props.showServiceLogs }}>
<Comment>
<Comment.Group>
{this.props.serviceNotes.map((serviceNote) => (
<Comment.Content>
<Comment.Author as="a">{serviceNote.created_at}</Comment.Author>
<Comment.Metadata>{serviceNote.user.username}</Comment.Metadata>
<Comment.Text>{serviceNote.entry}</Comment.Text>
</Comment.Content>
))}
<Form onSubmit={(e) => this.handleSubmit(e)}>
<Form.TextArea
style={{ height: "50px" }}
onChange={this.handleChange}
name="entry"
/>
<Form.Button
type="submit"
content="Add Note"
labelPosition="left"
icon="edit"
primary
/>
</Form>
</Comment.Group>
</Comment>
</div>
</>
);
}
}
const mapStateToProps = state => {
return {
services: state.services.services,
notes: state.notes.notes,
serviceNotes: state.serviceNotes.serviceNotes
};
};
const mapDispatchToProps = dispatch => {
return {
createNote: (entry, serviceId, userId) => dispatch(createNote(entry, serviceId, userId))
}
};
export default connect(mapStateToProps, mapDispatchToProps)(ServiceLogsComponent);
So I cannot updated the DOM when I create a new note. I tried this in these 2 reducers:
const initialState = {
notes: [],
};
export const notesReducer = (state = initialState, action) => {
switch (action.type) {
case "GET_NOTES":
return { ...state, notes: action.payload };
case "CREATE_NOTE":
return {
...state,
notes: [...state.notes, action.payload],
};
default:
return state;
}
};
and
const initialState = {
serviceNotes: [],
};
export const serviceNotesReducer = (state = initialState, action) => {
switch (action.type) {
case "GET_SERVICE_NOTES":
return { ...state, serviceNotes: action.payload };
case "CREATE_SERVICE":
return { ...state, serviceNotes: [ ...state.serviceNotes, action.payload] }
default:
return state;
}
};
Hope this is clear enough. In short: I need ServiceLogs state to change on CRUD action.
Maybe you should follow the recommendations of the Redux Style Guide: https://redux.js.org/style-guide/style-guide
I guess the idea is to keep just one store for the entire app and you should wrap the connection to the store in a higher level (componently speaking) using the component.
I would also recommend for you to use the new Redux Hooks like useSelector and convert your class components to use React Hooks to simplify your code.
https://reactjs.org/docs/hooks-intro.html

Is it possible to get the name of a nested, inner React Component by calling a function from one of its props?

I have this component:
class DashboardPage extends Component {
constructor(props) {
super(props);
this.state = {
loading: true,
shownPage: ActiveDeals,
error: false,
errorDetails: null,
activeIcon: "Home"
};
}
componentDidMount() {
//
}
setShownPage = (name, iconName) => () => {
this.setState({ shownPage: name, activeIcon: iconName });
};
getIconColor = () => {
// could I call this from the Home component and check its name? Or know the caller?
return "primary";
};
render() {
const { classes } = this.props;
const menuItems = (
<List>
<ListItem className={classNames(classes.listItem)} button onClick={this.setShownPage(ActiveDeals, "Home")}>
<ListItemIcon className={classNames(classes.listItemIcon)}>
<Home color={this.state.activeIcon === "Home" ? "primary" : "secondary"} />
</ListItemIcon>
</ListItem>
<ListItem className={classNames(classes.listItem)} button onClick={this.setShownPage(UpcomingDates, "CalendarToday")}>
<ListItemIcon className={classNames(classes.listItemIcon)}>
<CalendarToday color={this.state.activeIcon === "CalendarToday" ? "primary" : "secondary"} />
</ListItemIcon>
</ListItem>
<ListItem className={classNames(classes.listItem)} button onClick={this.setShownPage(DealsPipeline, "FilterList")}>
<ListItemIcon className={classNames(classes.listItemIcon)}>
<FilterList color={this.state.activeIcon === "FilterList" ? "primary" : "secondary"} />
</ListItemIcon>
</ListItem>
</List>
);
return (
<MainFrame
route={this.props.match.url}
title={this.state.shownPage.title}
menuItems={menuItems}
open={this.state.open}
topRightFeature={this.state.shownPage.topRightFeature}
>
<this.state.shownPage />
<div>Primary color is {this.props.theme.palette.primary.main}</div>
</MainFrame>
);
}
}
export default withStyles(styles, { withTheme: true })(DashboardPage);
... I'm used to using nameof() and type() in backend languages to know the name a given instance. In React, I have yet to find a way to do this.
Instead of setting the icon colors based on state (which uses hardcoded strings, yuck), I want a functional way to either traverse the dom tree to find relative children, or a way to just know the name of the icon which calls the getIconColor method so I can compare it to the active state.
Is there any way for a component to set a property while using a function that "knows" that it was called from e.g. Home?
I think you're trying to solve this in a way that misses out on the declarative power of React and the possibilities that component composition provides. The code duplication between the list items is begging for another component to be introduced:
const listItemStyles = {
listItem: {
/* whatever styles you need */
},
listItemIcon: {
/* whatever styles you need */
}
};
const DashboardListItem = withStyles(listItemStyles)(
({ Page, Icon, ShownPage, classes, setShownPage }) => {
return (
<ListItem
className={classes.listItem}
button
onClick={() => setShownPage(Page)}
>
<ListItemIcon className={classes.listItemIcon}>
<Icon color={ShownPage === Page ? "primary" : "secondary"} />
</ListItemIcon>
</ListItem>
);
}
);
Then your menuItems becomes:
const menuItems = [
{ Page: ActiveDeals, Icon: Home },
{ Page: UpcomingDates, Icon: CalendarToday },
{ Page: DealsPipeline, Icon: FilterList }
];
const mappedMenuItems = menuItems.map((menuItem, index) => (
<DashboardListItem
key={index}
{...menuItem}
ShownPage={this.state.shownPage}
setShownPage={this.setShownPage}
/>
));
With the full code looking like this:
import React, { Component } from "react";
import List from "#material-ui/core/List";
import ListItem from "#material-ui/core/ListItem";
import ListItemIcon from "#material-ui/core/ListItemIcon";
import { withStyles } from "#material-ui/core/styles";
import Home from "#material-ui/icons/Home";
import CalendarToday from "#material-ui/icons/CalendarToday";
import FilterList from "#material-ui/icons/FilterList";
const styles = {};
const ActiveDeals = () => {
return <div>ActiveDeals Page!</div>;
};
const UpcomingDates = () => {
return <div>UpcomingDates Page!</div>;
};
const DealsPipeline = () => {
return <div>DealsPipeline Page!</div>;
};
const listItemStyles = {
listItem: {
/* whatever styles you need */
},
listItemIcon: {
/* whatever styles you need */
}
};
const DashboardListItem = withStyles(listItemStyles)(
({ Page, Icon, ShownPage, classes, setShownPage }) => {
return (
<ListItem
className={classes.listItem}
button
onClick={() => setShownPage(Page)}
>
<ListItemIcon className={classes.listItemIcon}>
<Icon color={ShownPage === Page ? "primary" : "secondary"} />
</ListItemIcon>
</ListItem>
);
}
);
const menuItems = [
{ Page: ActiveDeals, Icon: Home },
{ Page: UpcomingDates, Icon: CalendarToday },
{ Page: DealsPipeline, Icon: FilterList }
];
class DashboardPage extends Component {
constructor(props) {
super(props);
this.state = {
shownPage: ActiveDeals
};
}
setShownPage = page => {
this.setState({ shownPage: page });
};
render() {
const mappedMenuItems = menuItems.map((menuItem, index) => (
<DashboardListItem
key={index}
{...menuItem}
ShownPage={this.state.shownPage}
setShownPage={this.setShownPage}
/>
));
return (
<div>
<List>{mappedMenuItems}</List>
<this.state.shownPage />
<div>Primary color is {this.props.theme.palette.primary.main}</div>
</div>
);
}
}
export default withStyles(styles, { withTheme: true })(DashboardPage);
Here's a working example:
You can get the identity of the caller if you bind the function in the component where it is used. This would work only in a class component.
Something like this:
class Apple extends React.Component {
constructor(props) {
super(props);
this.getName = props.getName.bind(this);
}
render() {
return <div>I am an {this.getName()}</div>;
}
}
class Banana extends React.Component {
getName() {
return this.constructor.name;
}
render() {
return (
<div className="App">
<Apple getName={this.getName} />
</div>
);
}
}
https://codesandbox.io/s/247lpxl4j0

App.js is not defined in react project

I am building a project in reactJS framework and when I had one big class App i decided to divide into a few classes. After changes I can see below error
'App' is not defined
Can anybody help me with this problem?
I tried all webpack settings but it doesn't help. It appears only after dividing the class 'App' but, before it was working fine.
Here is my code.
import React, { Component } from 'react';
import './App.css';
class App extends Component {
constructor(props){
super(props);
this.state = {
list,
searchTerm: "",
};
this.onDismiss = this.onDismiss.bind(this);
this.onSearchChange = this.onSearchChange.bind(this);
}
onSearchChange(event){
this.setState({ searchTerm: event.target.value });
}
onDismiss(id) {
const isNotId = item => item.objectID !== id;
const updatedList = this.state.list.filter(isNotId);
this.setState({ list: updatedList });
}
render() {
const { searchTerm, list } = this.state;
return (
<div className="App">
<Search
value = {searchTerm}
onChange = {this.onSearchChange}
/>
<Table
list = {list}
pattern = {searchTerm}
onDismiss = {this.onDismiss}
/>
</div>
);
}
}
class Search extends Component {
render(){
const { value, onChange } = this.props;
return(
<form>
<input
type = "text"
value = "value"
onChange = {onChange}
/>
</form>
);
}
}
class Table extends Component {
render(){
const { list, pattern, onDismiss } = this.props;
return(
<div>
{list.filter(isSearched(pattern)).map(item =>
<div key={item.objectID}>
<span>
<a href={item.url}>{item.title}</a>
</span>
<span>{item.author}</span>
<span>{item.num_comments}</span>
<span>{item.points}</span>
<span>
<button onClick={() => onDismiss(item.objectID)} type="button">
Delete
</button>
</span>
</div>
)}
</div>
);
}
};
}
export default App;
The answer you'll need is here
Few things I would like to explain. Check my comments in the code below
import React, { Component } from 'react';
import './App.css'; // have proper naming conventions change it to lowercase app.css
export default class App extends Component {
constructor(props){
super(props);
this.state = {
list,
searchTerm: "",
};
//Manual binding are ok but if you use arrow function you can stay away with scope related issues like let that = this;
//this.onDismiss = this.onDismiss.bind(this);
//this.onSearchChange = this.onSearchChange.bind(this);
}
onSearchChange = (event) => {
this.setState({ searchTerm: event.target.value });
}
onDismiss = (id) => {
const isNotId = item => item.objectID !== id;
const updatedList = this.state.list.filter(isNotId);
this.setState({ list: updatedList });
}
render() {
const { searchTerm, list } = this.state;
return (
<div className="App"> //Follow naming conventions chang classname App to app
<Search
value = {searchTerm}
onChange = {this.onSearchChange}
/>
<Table
list = {list}
pattern = {searchTerm}
onDismiss = {this.onDismiss}
/>
</div>
);
}
}
//you need to export your component to make it available to other components
export class Search extends Component {
render(){
const { value, onChange } = this.props;
return(
<form>
<input
type = "text"
value = "value"
onChange = {onChange}
/>
</form>
);
}
}
//you need to export your component to make it available to other components
export class Table extends Component {
render(){
const { list, pattern, onDismiss } = this.props;
return(
<div>
{list.filter(isSearched(pattern)).map(item =>
<div key={item.objectID}>
<span>
<a href={item.url}>{item.title}</a>
</span>
<span>{item.author}</span>
<span>{item.num_comments}</span>
<span>{item.points}</span>
<span>
<button onClick={() => onDismiss(item.objectID)} type="button">
Delete
</button>
</span>
</div>
)}
</div>
);
}
};
}

Resources