How do you use a <Toast> in blueprintjs? - reactjs

As someone who isn't using typescript, I'm having a really hard time figuring out how to use Toast. I can tell this component seems to look different than the others.
Here is the example code, what would be the ES6 equivalent?
import { Button, Position, Toaster } from "#blueprintjs/core";
class MyComponent extends React.Component<{}, {}> {
private toaster: Toaster;
private refHandlers = {
toaster: (ref: Toaster) => this.toaster = ref,
};
public render() {
return (
<div>
<Button onClick={this.addToast} text="Procure toast" />
<Toaster position={Position.TOP_RIGHT} ref={this.refHandlers.toaster} />
</div>
)
}
private addToast = () => {
this.toaster.show({ message: "Toasted!" });
}
}

TypeScript just adds type checks to the code. You can learn more about it from this question.
The change is pretty simple. You just need to remove type declarations.
import { Button, Position, Toaster } from "#blueprintjs/core";
class MyComponent extends React.Component {
refHandlers = {
toaster: (ref) => this.toaster = ref,
};
render() {
return (
<div>
<Button onClick={this.addToast} text="Procure toast" />
<Toaster position={Position.TOP_RIGHT} ref={this.refHandlers.toaster} />
</div>
)
}
addToast = () => {
this.toaster.show({ message: "Toasted!" });
}
}

Related

React- Destruct State error in Typescript

I am trying to implement a react component for AutoComplete by referring to a Tutorial.I am using Typescript for my development. When I try to destruct the state inside the render suggestions method the TSLint compiler shows an error saying 'Property Suggestions does not exist on type {}'. Could any one help me to overcome this. It is a big big help.
import * as React from 'react';
export default class Autocomplete extends React.Component<{},{}> {
items: string[];
constructor( props){
super(props);
this.state = {
suggestions :[],
};
this.items =['David','Damian','sara'];
}
render() {
return (
<div>
<input
type="text" onChange={this.onTextChanged}
/>
{this.renderSuggestions()}
</div>
);
}
onTextChanged = (e) =>{
console.log(e.target.value);
const value = e.target.value;
let suggestions =[];
if(value.legth>0){
const regex = new RegExp(`^${value}`,'i');
suggestions = this.items.sort().filter(v => regex.test(v));
}
this.setState(()=> ({suggestions}));
}
renderSuggestions() {
const {suggestions } = this.state;
if(suggestions.length === 0){
return null;
}
return (
<ul>
{suggestions.map((item)=> <li>{item}</li>)}
</ul>
);
}
}
[EDIT : Attached the Screen Capture]
The error was in your
if(value.legth>0){}, you missed n for length. It was just a typo error.
Also I tried the typescript version and this was the only mistake
Link to typescript version
Updated working code is
import * as React from 'react';
export default class Autocomplete extends React.Component {
items=[];
constructor( props){
super(props);
this.state = {
suggestions :[],
};
this.items =['David','Damian','sara'];
}
render() {
return (
<div>
<input
type="text" onChange={this.onTextChanged}
/>
{this.renderSuggestions()}
</div>
);
}
onTextChanged = (e) =>{
console.log(e.target.value);
const value = e.target.value;
let suggestions =[];
if(value.length>0){
const regex = new RegExp(`^${value}`,'i');
suggestions = this.items.sort().filter(v => regex.test(v));
}
this.setState(()=> ({suggestions}));
}
renderSuggestions() {
const {suggestions } = this.state;
if(suggestions.length === 0){
return null;
}
return (
<ul>
{suggestions.map((item)=> <li>{item}</li>)}
</ul>
);
}
}
You specify in your component that the state is of type {} by doing React.Component<{}>. You can usually avoid this by letting typescript infer the state but it has a bit of a problem doing that when the state is assigned inside the constructor. You can usually fix these by assigning them directly as fields inside the class like
export default class Autocomplete extends React.Component {
items = ['David','Damian','sara']
state = { suggestions :[] }
// rest of your component here
}
You can do this in your current class because you don't use the constructor for anything other than setting state, but if that's not an option, you can make sure typescript understands the state properly by assigning it as an explicit generic argument for your component like such.
interface AutocompleteState {
suggestions: Suggestion[]
}
export default class Autocomplete extends React.Component<AutocompleteState> {
constructor() {
// ...
}
}
By Considering all the comments and bit of struggling and some readings I made it work. Thank you for all your suggestions.
import * as React from 'react';
import { Suggestions } from 'office-ui-fabric-react';
export interface IServiceState {
suggestions ?: string []
}
export default class Autocomplete extends React.Component<{},IServiceState> {
items =['David','Damian','sara'];
constructor( props){
super(props);
this.state = {
suggestions : [],
};
}
render() {
return (
<div>
<input
type="text" onChange={this.onTextChanged}
/>
{this.renderSuggestions()}
</div>
);
}
onTextChanged = (e) =>{
console.log(e.target.value);
const value = e.target.value;
let suggestions :string[];
if(value.length>0){
const regex = new RegExp(`^${value}`,'i');
suggestions = this.items.sort().filter(v => regex.test(v));
console.log(suggestions);
}
//this.setState(()=> ({suggestions1}));
//this.setState()
this.setState({suggestions : suggestions});
}
renderSuggestions() {
const suggestions :string[]= this.state.suggestions;
console.log('render Suggestions');
console.log(this.state.suggestions);
console.log('render1 Suggestions');
if(Suggestions.length === 0){
return null;
}
return (
<ul>
{suggestions.map((item)=> <li>{item}</li>)}
</ul>
);
}
}

Converting functional component to class component

I have one functional component, but as I need to use now state and more complex logic, I would like to convert it to class component.
But I don't know exactly how to get it working:
My functional component:
import React from 'react';
const FileList = (props) => {
const items = props.items.map((item) => {
return <p key={item.reqId} > { item.name }</ p>
});
return <div>{items}</div>
}
And I tried to do that:
export default class FileL extends React.Component {
constructor(props) {
super(props)
}
render() {
const { items } = this.props;
items = props.items.map((item) => {
return <p key={item.reqId} > {item.name}</ p>
});
return (
<div>{items}</div>
);
}
}
But this is not working.It says "items" is read-only.
I would like to keep the same functionality.
Any ideas?
In your render function
render() {
const { items } = this.props;
items = props.items.map((item) => {
return <p key={item.reqId} > {item.name}</ p>
});
return (
<div>{items}</div>
);
}
items is const so you can't override it. This has nothing to do with React. And you shouldn't reassign a props element, even if its defined with let. You might use the following:
render() {
const { items } = this.props;
return (
<div>
{
items.map((item) => <p key={item.reqId} > {item.name}</ p>)
}
</div>
);
}
You can try this,
export default class FileL extends React.Component {
constructor(props) {
super(props);
}
render() {
return (
<div>
{
this.props.items.map((item) => {
return <p key={item.reqId} > {item.name}</ p>
})
}
</div>
);
}
}
Actually you don't need to convert your component to class based component, as React 16.8 comes with Hooks. Using Hooks you can do whatever you can do with class based component. They let you use state and other React features without writing a class.

React Context API can't bind 'this'

When I type anything into the input which is inside of the <Input> component, I wanna execute the handleInput() function which is inside of the <MainProvider> component.
This (onChange={store.handleInput.bind(this)}) looks like working but it can't pass this.
In the console I just get undefined message.
Here's an example code.
const MainContext = React.createContext()
class MainProvider extends React.Component {
handleInput (e) {
console.log(e)
}
render () {
const store = {
handleInput: () => this.handleInput()
}
return (
<MainContext.Provider value={store}>
{this.props.children}
</MainContext.Provider>
)
}
}
class Input extends React.Component {
render () {
return (
<MainContext.Consumer>
{(store) => (
<input {...this.props} onChange={store.handleInput.bind(this)} />
)}
</MainContext.Consumer>
)
}
}
class App extends React.Component {
render () {
return (
<MainProvider>
<Input name='one' />
<Input name='two' />
</MainProvider>
)
}
}
How can I pass this in the onChange event? I'm using React 16.3.1.
The problem comes because you have used arrow function in MainProvider component which overrides the context being passed when you are calling function
render () {
const store = {
handleInput: () => this.handleInput() // using arrow function here overrides the contect
}
}
Change it to
class MainProvider extends React.Component {
handleInput (e) {
console.log(e)
}
render () {
const store = {
handleInput: this.handleInput
}
return (
<MainContext.Provider value={store}>
{this.props.children}
</MainContext.Provider>
)
}
}
However in this case you would explicitly need to bind from child components or it will take the context from where it is called.

Pass state value to component

I am really new in React.js. I wanna pass a state (that i set from api data before) to a component so value of selectable list can dynamically fill from my api data. Here is my code for fetching data :
getListSiswa(){
fetch('http://localhost/assessment-app/adminpg/api/v1/Siswa/')
.then(posts => {
return posts.json();
}).then(data => {
let item = data.posts.map((itm) => {
return(
<div key={itm.siswa_id}>
<ListItem
value={itm.siswa_id}
primaryText={itm.nama}
/>
</div>
)
});
this.setState({item: item});
});
}
From that code, i set a state called item. And i want to pass this state to a component. Here is my code :
const ListSiswa = () => (
<SelectableList>
<Subheader>Daftar Siswa</Subheader>
{this.state.item}
</SelectableList>
);
But i get an error that say
TypeError: Cannot read property 'item' of undefined
I am sorry for my bad explanation. But if you get my point, i am really looking forward for your solution.
Here is my full code for additional info :
import React, {Component} from 'react';
import PropTypes from 'prop-types';
import {List, ListItem, makeSelectable} from 'material-ui/List';
import Subheader from 'material-ui/Subheader';
let SelectableList = makeSelectable(List);
function wrapState(ComposedComponent) {
return class SelectableList extends Component {
static propTypes = {
children: PropTypes.node.isRequired,
};
getListSiswa(){
fetch('http://localhost/assessment-app/adminpg/api/v1/Siswa/')
.then(posts => {
return posts.json();
}).then(data => {
let item = data.posts.map((itm) => {
return(
<div key={itm.siswa_id}>
<ListItem
value={itm.siswa_id}
primaryText={itm.nama}
/>
</div>
)
});
this.setState({item: item});
});
}
componentWillMount() {
this.setState({
selectedIndex: this.props.defaultValue,
});
this.getListSiswa();
}
handleRequestChange = (event, index) => {
this.setState({
selectedIndex: index,
});
};
render() {
console.log(this.state.item);
return (
<ComposedComponent
value={this.state.selectedIndex}
onChange={this.handleRequestChange}
>
{this.props.children}
</ComposedComponent>
);
}
};
}
SelectableList = wrapState(SelectableList);
const ListSiswa = () => (
<SelectableList>
<Subheader>Daftar Siswa</Subheader>
{this.state.item}
</SelectableList>
);
export default ListSiswa;
One way to do it is by having the state defined in the parent component instead and pass it down to the child via props:
let SelectableList = makeSelectable(List);
function wrapState(ComposedComponent) {
return class SelectableList extends Component {
static propTypes = {
children: PropTypes.node.isRequired,
};
componentWillMount() {
this.setState({
selectedIndex: this.props.defaultValue,
});
this.props.fetchItem();
}
handleRequestChange = (event, index) => {
this.setState({
selectedIndex: index,
});
};
render() {
console.log(this.state.item);
return (
<ComposedComponent
value={this.state.selectedIndex}
onChange={this.handleRequestChange}
>
{this.props.children}
{this.props.item}
</ComposedComponent>
);
}
};
}
SelectableList = wrapState(SelectableList);
class ListSiswa extends Component {
state = {
item: {}
}
getListSiswa(){
fetch('http://localhost/assessment-app/adminpg/api/v1/Siswa/')
.then(posts => {
return posts.json();
}).then(data => {
let item = data.posts.map((itm) => {
return(
<div key={itm.siswa_id}>
<ListItem
value={itm.siswa_id}
primaryText={itm.nama}
/>
</div>
)
});
this.setState({item: item});
});
}
render() {
return (
<SelectableList item={this.state.item} fetchItem={this.getListSiswa}>
<Subheader>Daftar Siswa</Subheader>
</SelectableList>
);
}
}
export default ListSiswa;
Notice that in wrapState now I'm accessing the state using this.props.item and this.props.fetchItem. This practice is also known as prop drilling in React and it will be an issue once your app scales and multiple nested components. For scaling up you might want to consider using Redux or the Context API. Hope that helps!
The error is in this component.
const ListSiswa = () => (
<SelectableList>
<Subheader>Daftar Siswa</Subheader>
{this.state.item}
</SelectableList>
);
This component is referred as Stateless Functional Components (Read)
It is simply a pure function which receives some data and returns the jsx.
you do not have the access this here.

JSX props should not use .bind()

How to fix this error when I have the binding this way: previously binding in constructor solved but this is a bit complex for me:
{this.onClick.bind(this, 'someString')}>
and
<form onSubmit={handleSubmit(this.handleFormSubmit.bind(this))}>
Option 1:
Use arrow functions (with babel-plugins)
PS:- Experimental feature
class MyComponent extends Component {
handleClick = (args) => () => {
// access args here;
// handle the click event
}
render() {
return (
<div onClick={this.handleClick(args)}>
.....
</div>
)
}
}
Option 2: Not recommended
Define arrow functions in render
class MyComponent extends Component {
render() {
const handleClick = () => {
// handle the click event
}
return (
<div onClick={handleClick}>
.....
</div>
)
}
}
Option 3:
Use binding in constructor
class MyComponent extends Component {
constructor() {
super();
this.handleClick = this.handleClick.bind(this);
}
handleClick() {
// handle click
}
render() {
return (
<div onClick={this.handleClick}>
.....
</div>
)
}
}
I recommend you to use binding in the class constructor. This will avoid inline repetition (and confusion) and will execute the "bind" only once (when component is initiated).
Here's an example how you can achieve cleaner JSX in your use-case:
class YourComponent extends React.Component {
constructor(props) {
super(props);
// Bind functions
this.handleClick = this.handleClick.bind(this);
this.handleSubmit = this.handleSubmit.bind(this);
}
handleClick() {
this.onClick('someString');
}
onClick(param) {
// That's your 'onClick' function
// param = 'someString'
}
handleSubmit() {
// Same here.
this.handleFormSubmit();
}
handleFormSubmit() {
// That's your 'handleFormSubmit' function
}
render() {
return (
<form onSubmit={this.handleSubmit}>
<p>...</p>
<button onClick={this.handleClick} type="button">Cancel</button>
<button type="submit">Go!</button>
</form>
);
}
}
Even though all the previous answers can achieve the desire result, but I think the snippet below worth mentioning.
class myComponent extends PureComponent {
handleOnclickWithArgs = arg => {...};
handleOnclickWithoutArgs = () => {...};
render() {
const submitArg = () => this.handleOnclickWithArgs(arg);
const btnProps = { onClick: submitArg }; // or onClick={submitArg} will do
return (
<Fragment>
<button {...btnProps}>button with argument</button>
<button onClick={this.handleOnclickWithoutArgs}>
button without argument
</button>
</Fragment>
);
}
}

Resources