I have a method called: onChangeText
It means every time I type, it will search the remote api.
How do I delay the remote api call? i.e. let user types certain things, then connect the api, rather than connect every key stroke.
onChangeText(title) {
console.log('-- chg text --');
console.log(title);
this.props.searchApi(title);
}
The component:
import React, { Component } from 'react';
import { SearchBar, Divider } from 'react-native-elements';
import { View, ScrollView, Text, StyleSheet, Image} from 'react-native';
import { connect } from 'react-redux';
// action creator
import { searchApi } from './reducer';
class SearchContainer extends Component {
constructor(props) {
super(props);
}
onChangeText(title) {
console.log('-- chg text --');
console.log(title);
this.props.searchApi(title);
}
onClearText(e) {
console.log('-- clear text --');
console.log(e);
}
render() {
const { } = this.props;
const containerStyle = {
display: 'flex',
justifyContent: 'center',
alignItems: 'center'
}
const searchStyle = {
width: 300,
height: 45
};
return (
<View
style={containerStyle}
>
<Image
source={require('../../asset/img/logo.png')}
style={{
height: 150,
width: 150
}}
/>
<SearchBar
cancelButtonTitle="Cancel"
placeholder='Search'
containerStyle={searchStyle}
onChangeText={this.onChangeText.bind(this)}
onClearText={this.onClearText.bind(this)}
/>
</View>
);
}
}
const mapStateToProps = state => {
return {
};
};
const mapDispatchToProps = dispatch => {
return {
searchApi: () => dispatch(searchApi())
}
};
export default connect(mapStateToProps, mapDispatchToProps)(SearchContainer);
Use lodash debounce. It is used for this exact use case
Sample React example. Should be able to port to native the same way
import React, {Component} from 'react'
import { debounce } from 'lodash'
class TableSearch extends Component {
//********************************************/
constructor(props){
super(props)
this.state = {
value: props.value
}
this.changeSearch = debounce(this.props.changeSearch, 250)
}
//********************************************/
handleChange = (e) => {
const val = e.target.value
this.setState({ value: val }, () => {
this.changeSearch(val)
})
}
//********************************************/
render() {
return (
<input
onChange = {this.handleChange}
value = {this.props.value}
/>
)
}
//********************************************/
}
Related
I'am using typescript 3.
I have this map component :
import * as React from 'react';
import GoogleMapReact from 'google-map-react';
import { any } from 'prop-types';
import {Coords} from 'google-map-react';
export interface HelloProps { center: Coords ; zoom: number ; }
const AnyReactComponent = ( {text} : { lat: any,text:any,lng:any}) => <div>{text}</div>;
export class SimpleMap extends React.Component <HelloProps, {}> {
defaultProps = {
center: {
lat: 59.95,
lng: 30.33
},
zoom: 11
};
myCallbackMap = (dataFromChild:number) => {
this.defaultProps.zoom = dataFromChild;
}
render() {
return (
// Important! Always set the container height explicitly
<div style={{ height: '100vh', width: '100%' }}>
<GoogleMapReact
bootstrapURLKeys={{ key: 'AIzaSyDs5u7u1Almh8-Jch3cHzuWB-egVPTZajs' }}
defaultCenter={this.props.center}
defaultZoom={this.props.zoom}
>
<AnyReactComponent
lat={59.955413}
lng={30.337844}
text="My Marker"
/>
</GoogleMapReact>
</div>
);
}
}
export default SimpleMap;
And i want to use :
myCallbackMap = (dataFromChild:number) => {
this.defaultProps.zoom = dataFromChild;
}
In my search bar so when someone search it change the map.
Here is my search file (scroll down to see the call) :
import * as React from 'react';
import axios from 'axios'
import Suggestions from './Suggestions'
import myCallbackMap from './Map'
const API_KEY:string = "process.env"
const API_URL:string = 'http://127.0.0.1:9001/v1/test'
export class Search extends React.Component{
state = {
query: '' as string,
results : [] as any[]
}
search = {
value: '' as string,
}
getInfo = () => {
axios.post(`${API_URL}/${this.state.query}/`)
.then(({ data }) => {
this.setState({
results: data.data
})
})
}
handleInputChange = () => {
this.setState({
query: this.search.value
}, () => {
if (this.state.query && this.state.query.length > 1) {
if (this.state.query.length % 2 === 0) {
this.getInfo()
}
} else if (!this.state.query) {
}
})
}
myCallbackSearch = (dataFromChild:string) => {
this.search.value = dataFromChild;
// here where i want ot use the map call back
myCallbackMap(111);
}
render() {
return (
<form>
<input
placeholder="Search for..."
ref={input => this.search = input}
onChange={this.handleInputChange}
/>
<Suggestions results={this.state.results} callbackFromParentSearch={this.myCallbackSearch}/>
</form>
)
}
}
export default Search
How to use and import properly the map call back and use it in the search component ? The main goal is that when someone type in the search bar it change the map dynamicaly. but now i'am working on a simple prototype.
Regards
You cannot import it because it would be a static function when you need an instance function.
You have to pass the callback as a prop of the search component.
The callback itself actually looks like an event handler which role is to change the default props (which should be declared static) of the google map in another component. So my guess is you don't actually want to change the default props of the map but rather give it a new zoom value via props.
You can either:
Implement the handler in the common ancestor to both the search and the map component, make the zoom a state of this common ancestor, update this state in the callback, and propagate through props the callback all the way to the search component, and the zoom value all the way to the map component.
Or:
Put this state in a redux store which will allow you to skip the props chaining and directly register map to listen for zoom change in the store, and pass an action creator to the search component so that it can notify the store when the search change.
Common ancestor version:
import * as React from 'react';
import axios from 'axios';
import Suggestions from './Suggestions'
const API_KEY:string = "process.env"
const API_URL:string = 'http://127.0.0.1:9001/v1/test'
export class Search extends React.Component{
state = {
query: '' as string,
results : [] as any[]
}
search = {
value: '' as string,
}
getInfo = () => {
axios.post(`${API_URL}/${this.state.query}/`)
.then(({ data }) => {
this.setState({
results: data.data
})
})
}
handleInputChange = () => {
this.setState({
query: this.search.value
}, () => {
if (this.state.query && this.state.query.length > 1) {
if (this.state.query.length % 2 === 0) {
this.getInfo()
}
} else if (!this.state.query) {
}
})
}
myCallbackSearch = (dataFromChild:string) => {
this.search.value = dataFromChild;
// here where i want ot use the map call back
myCallbackMap(111);
}
render() {
const { callbackFromParentSearch } = this.props;
return (
<form>
<input
placeholder="Search for..."
ref={input => this.search = input}
onChange={this.handleInputChange}
/>
<Suggestions results={this.state.results} callbackFromParentSearch={callbackFromParentSearch}/>
</form>
)
}
}
export default Search;
Note that the components don't need to have the same exact parent below is just an example. It will still work if you pass the props from parent to child until you get to the targets Search and SimpleMap.
export default class CommonAncestor extends Component {
state: {
zoom: 3 as number,
}
handleCallbackFromParentSearch = (dataFromChild:number) => {
this.setState(() => ({ zoom: dataFromChild }));
}
render() {
const { zoom } = this.state;
return (
<React.Fragment>
<Search callbackFromParentSearch={this.handleCallbackFromParentSearch} />
<SimpleMap zoom={zoom} />
</React.Fragment>
);
}
}
Redux version:
Search component connected to the changeZoom action
import * as React from 'react';
import axios from 'axios';
import { connect } from 'react-redux';
import Suggestions from './Suggestions';
import { actionCreators as zoomAC } from 'src/redux/zoom';
const API_KEY:string = "process.env"
const API_URL:string = 'http://127.0.0.1:9001/v1/test'
class Search extends React.Component{
state = {
query: '' as string,
results : [] as any[]
}
search = {
value: '' as string,
}
getInfo = () => {
axios.post(`${API_URL}/${this.state.query}/`)
.then(({ data }) => {
this.setState({
results: data.data
})
})
}
handleInputChange = () => {
this.setState({
query: this.search.value
}, () => {
if (this.state.query && this.state.query.length > 1) {
if (this.state.query.length % 2 === 0) {
this.getInfo()
}
} else if (!this.state.query) {
}
})
}
myCallbackSearch = (dataFromChild:string) => {
this.search.value = dataFromChild;
// here where i want ot use the map call back
myCallbackMap(111);
}
render() {
const { changeZoom} = this.props;
return (
<form>
<input
placeholder="Search for..."
ref={input => this.search = input}
onChange={this.handleInputChange}
/>
<Suggestions results={this.state.results} callbackFromParentSearch={changeZoom}/>
</form>
)
}
}
export default connect(
null,
dispatch => ({
changeZoom: value => dispatch(zoomAC.changeZoom(value))
})
)(Search);
SimpleMap component connected to the zoom store:
import * as React from 'react';
import GoogleMapReact from 'google-map-react';
import { connect } from 'react-redux';
import { any } from 'prop-types';
import {Coords} from 'google-map-react';
export interface HelloProps { center: Coords ; zoom: number ; }
const AnyReactComponent = ( {text} : { lat: any,text:any,lng:any}) => <div>{text}</div>;
export class SimpleMap extends React.Component <HelloProps, {}> {
defaultProps = {
center: {
lat: 59.95,
lng: 30.33
},
zoom: 11
};
render() {
return (
// Important! Always set the container height explicitly
<div style={{ height: '100vh', width: '100%' }}>
<GoogleMapReact
bootstrapURLKeys={{ key: 'AIzaSyDs5u7u1Almh8-Jch3cHzuWB-egVPTZajs' }}
defaultCenter={this.props.center}
defaultZoom={this.props.zoom}
>
<AnyReactComponent
lat={59.955413}
lng={30.337844}
text="My Marker"
/>
</GoogleMapReact>
</div>
);
}
}
export default connect(
state => ({ zoom: state.zoom })
)(SimpleMap);
Hello I am trying to make a step wizard component but I have the following issue. I have the following file:
import React from 'react';
import { View } from 'react-native';
import WizardStep from './WizardStep'
export default class Wizard extends React.Component {
constructor(props){
super(props);
this.state = {
questions: this.props.questions,
answers: this.props.answers,
totalSteps: this.props.questions.length,
currentStep: 0,
results: []
}
}
updateStep = answer => {
newResults = this.state.results
newResults[this.state.currentStep - 1] = answer
this.setState({
results: newResults,
currentStep: this.state.currentStep + 1
}, () => {
if (this.state.currentStep == this.state.totalSteps) {
this.props.finish();
}
})
}
renderStep = () => {
if (this.state.currentStep < this.state.totalSteps) {
return (
<View>
<WizardStep
question={this.state.questions[this.state.currentStep]}
answers={this.state.answers[this.state.currentStep]}
step={this.state.currentStep}
totalSteps={this.state.totalSteps}
updateStep={this.updateStep}
/>
</View>
);
} else {
return null;
}
}
render(){
return(
<View>
{this.renderStep()}
</View>
)
}
}
questions is an array of strings and answers is an array of arrays of strings.
Anyway the first screen shows up just fine. But when I call the updateStep function the currentStep updates but it doesn't show the 2nd item from questions/answers array. Any ideas? Thank you in advance!
Adding the other components for the wizard:
import React from 'react';
import { View, Text, StyleSheet } from 'react-native';
import { Button } from "react-native-elements";
import { Constants } from 'expo';
import WizardStepButton from './WizardStepButton';
export default class WizardStep extends React.Component {
constructor(props){
super(props);
this.state ={
question: this.props.question,
answers: this.props.answers,
totalSteps: this.props.totalSteps,
step: this.props.step,
}
}
renderAnswers = () => {
var answers = []
for (var i = 0; i < this.state.answers.length; i++) {
answers.push(
<WizardStepButton
answer={this.state.answers[i]}
updateStep={this.props.updateStep}
key={i}
/>
);
}
return answers;
}
render(){
return(
<View>
<Text style={styles.title}>Step {this.state.step + 1}/{this.state.totalSteps}</Text>
<Text style={styles.title}>{this.state.question}</Text>
{this.renderAnswers()}
</View>
)
}
}
const styles = StyleSheet.create({
title: {
marginTop: 30,
marginBottom: 30,
fontSize: 25,
color: 'rgba(96,100,109, 1)',
lineHeight: 24,
textAlign: 'center',
},
});
and the button component:
import React from 'react';
import { View, Text, StyleSheet } from 'react-native';
import { Button } from "react-native-elements";
import { Constants } from 'expo';
export default class WizardStepButton extends React.Component {
constructor(props){
super(props);
this.state ={
}
}
render(){
return(
<View>
<Button
style={{margin: 10}}
large
raised
title={this.props.answer}
onPress={() => this.props.updateStep(this.props.answer)}
/>
</View>
)
}
}
You should only increment state values by using a state updater function. - https://stackoverflow.com/a/45196079/874027
You're not spreading this.state.results before editing and putting them back into state.
Also the currentStep checks indexing looks off.
updateStep = answer => {
this.setState((state) => {
const { results, currentStep } = state
const newResults = [...results]
newResults[currentStep] = answer
return {
results: newResults,
currentStep: currentStep + 1,
}
}, () => {
const { currentStep, totalSteps } = this.state
if (currentStep + 1 === totalSteps) {
this.props.finish();
}
})
}
EDIT: in WizardStep component you're syncing props with state in constructor so when you try to pass the new props after you update your state, they'll never get reflected in the Wizard since its constructor has already fired off. You can either fix this by using props in your WizardStep component, or by passing it a key, so the new instance gets created every time the key changes, e.g.
<WizardStep
question={this.state.questions[this.state.currentStep]}
answers={this.state.answers[this.state.currentStep]}
step={this.state.currentStep}
totalSteps={this.state.totalSteps}
updateStep={this.updateStep}
key={this.state.currentStep}
/>
I've tested this locally and the steps do get changed with this approach.
I'm tring to use draftjs and want to crate a custom block component.
First question is, I can't perfectly complete an example which is create in document (link here).
When I click the button named 'bold', the editor loses focus and my text doesn't get bolder.
Here is my code:
import React, { Component } from 'react';
import { Editor, EditorState, RichUtils } from 'draft-js';
import { Paper, Button } from '#material-ui/core';
class ProductComponent extends Component {
render() {
const { src } = this.props;
return (
<div>{src}</div>
)
}
}
export default class MyEditor extends React.Component {
constructor(props) {
super(props);
this.state = {
editorState: EditorState.createEmpty()
}
}
onChange = (editorState) => this.setState({ editorState });
onBoldClick = () => {
this.onChange(RichUtils.toggleInlineStyle(this.state.editorState, 'BOLD'));
}
myBlockStyleFn = (contentBlock) => {
const type = contentBlock.getType();
if (type === 'product') {
return {
component: ProductComponent,
editable: false,
props: {
src: 'https://ss1.bdstatic.com/70cFvXSh_Q1YnxGkpoWK1HF6hhy/it/u=3307626454,3432420457&fm=27&gp=0.jpg',
},
}
}
}
render() {
const {editorState} = this.state;
return (
<Paper elevation={0}>
<Button onClick={this.onBoldClick}>bold</Button>
<Editor
blockStyleFn={this.myBlockStyleFn}
editorState={editorState}
onChange={this.onChange}
/>
</Paper>
);
}
}
I'm using react antd table with pagination,sort & etc. it'll fetch data when component is loaded. when te user clicks on pagination it should call Api and get new data accordingly. it's getting bada but the table won't update with new data.
I'm new with react. please help me with this,
import PropTypes from 'prop-types';
import React, { Component } from "react";
import { connect } from "react-redux";
import { bindActionCreators } from 'redux';
import { Row, Col } from "antd";
import clone from "clone";
import Button from "../../components/uielements/button";
import PageHeader from "../../components/utility/pageHeader";
import Box from "../../components/utility/box";
import LayoutWrapper from "../../components/utility/layoutWrapper";
import SignUpForm from "../../components/vendorSignup";
import basicStyle from "../../settings/basicStyle";
import actions from "../../redux/vendorSignUp/actions";
import { createColumns } from "./config";
import { ButtonWrapper } from "../../components/card/cardModal.style";
import SimpleTable from "./antTables/tableViews/sortView";
import ContentHolder from '../../components/utility/contentHolder';
import Spin from '../Feedback/Spin/spin.style';
const { addVendor, getVendors } = actions;
class Cards extends Component {
static propTypes = {
dispatch: PropTypes.func
};
constructor(props) {
super(props);
this.addColumn = this.addColumn.bind(this);
this.editColumn = this.editColumn.bind(this);
this.handleCancel = this.handleCancel.bind(this);
this.submitCard = this.submitCard.bind(this);
this.updateCard = this.updateCard.bind(this);
this.tableInfo = {
title: "Sort Table",
value: "sortView",
columns: []
};
this.tableInfo.columns = createColumns(this.editColumn);
this.state = {
editView: false,
selectedCard: null,
modalType: ""
};
}
componentWillMount() {
const { getVendors } = this.props.actions;
}
render() {
const style = {
textAlign: 'center',
background: '#f1f3f6',
padding: '30px 50px'
};
const { rowStyle, colStyle, gutter } = basicStyle;
const { editView, selectedCard, modalType } = this.state;
const vendorSignUp = clone(this.props.vendorSignUp);
if (vendorSignUp.length == 0) {
return (<LayoutWrapper>
<PageHeader>Vendors</PageHeader>
<ContentHolder>
<div style={style}>
<Spin spinning={vendorSignUp.length === 0} />
</div>
</ContentHolder>
</LayoutWrapper>);
}
return (
<LayoutWrapper>
<PageHeader>Vendors</PageHeader>
<Row style={rowStyle} gutter={gutter} justify="start">
<Col md={24} sm={24} xs={24} style={colStyle}>
<Box>
<ButtonWrapper className="isoButtonWrapper">
<Button type="primary" className="" onClick={this.addColumn}>
Add New Vendor
</Button>
</ButtonWrapper>
<SimpleTable columns={this.tableInfo.columns} dataSource={vendorSignUp} loading={this.loading} onChange={this.onChange} />
{selectedCard ? (
<SignUpForm
saveFormRef={this.saveFormRef}
editView={editView}
modalType={modalType}
onCancel={this.handleCancel}
onCreate={this.submitCard}
onOk={this.submitCard}
wrappedComponentRef={this.saveFormRef}
/>
) : ("")}
</Box>
</Col>
</Row>
</LayoutWrapper>
);
}
}
function mapStateToProps(state) {
return {
...state.vendorSignUp.toJS()
};
}
function mapDispatchToProps(dispatch) {
return {
actions: bindActionCreators({
getVendors, addVendor
}, dispatch)
}
}
export default connect(mapStateToProps, mapDispatchToProps)(Cards);
Here is my table view;
import React, { Component } from 'react';
import { connect } from 'react-redux';
import TableWrapper from '../antTable.style';
import actions from "../../../../redux/vendorSignUp/actions";
const { getVendors } = actions;
class SignIn extends Component {
constructor(props) {
super(props);
this.onChange = this.onChange.bind(this);
this.state = {
dataList: this.props.dataSource || this.props.dataList.getAll()
};
this.columns = this.props.columns || this.props.tableInfo.columns;
}
onChange(pagination, filters, sorter) {
if (sorter.order) {
if (sorter.order == "ascend") {
const { getVendors } = this.props;
this.setState({
loading: true,
});
getVendors('3-5');
}
else {
this.props.dataSource.sort(function (a, b) {
var x = a[sorter.field].toLowerCase();
var y = b[sorter.field].toLowerCase();
if (y < x) { return -1; }
if (y > x) { return 1; }
return 0;
});
}
}
}
render() {
return (
<TableWrapper
columns={this.columns}
onChange={this.onChange}
dataSource={this.state.dataList}
className="isoSortingTable"
pagination={{ pageSize: 10 }}
loading={this.state.loading}
/>
);
}
}
function mapStateToProps(state) {
return {
...state
};
}
export default connect(mapStateToProps,
{ getVendors }
)(SignIn);
problem was in the constructor.it's will be executed only one time. therefore this.state.datalist will be the same. that's why it did not work.
Iam new to react-native and aws appsync.We are trying to display a list of messages.But when i run react-native run-android it is throwing an error saying
TypeError: undefined is not an object(evaluating '_props.listMessagesQuery.listMessages')
[Below is the screenshot url of the error]
https://i.stack.imgur.com/b1Wlj.png
Chat.js
import React,{Component} from 'react';
import ChatInput from './ChatInput';
import ChatMessages from './ChatMessages';
import { graphql, compose } from 'react-apollo';
import listMessages from './querys/listMessages';
import createMessage from './querys/createMessage';
import gql from 'graphql-tag';
import {
Platform,
StyleSheet,
Text,
View,
scrollIntoView
} from 'react-native';
class Chat extends Component {
state = {
message: '',
}
render() {
return (
<View className='Chat'>
<ChatMessages
messages={this.props.listMessagesQuery.listMessages || []}
endRef={this._endRef}
/>
<ChatInput
message={this.state.message}
onTextInput={(message) => this.setState({message})}
onResetText={() => this.setState({message: ''})}
onSend={this._onSend}
/>
</View>
);
}
_onSend = () => {
//console.log(`Send: ${this.state.message}`)
this.props.createMessageMutation({
variables: {
text: this.state.message,
sentById: this.props.userId
}
})
}
/*
* AUTO SCROLLING
*/
_endRef = (element) => {
this.endRef = element
}
componentDidUpdate(prevProps) {
// scroll down with every new message
if (prevProps.listMessagesQuery.listMessages !== this.props.listMessagesQuery.listMessages && this.endRef) {
this.endRef.scrollIntoView()
}
}
}
export default compose(
graphql(listMessages, {
options: {
fetchPolicy: 'cache-and-network'
},
props: (props) => ({
posts: props.listMessagesQuery.listMessages && props.listMessagesQuery.listMessages.Message,
})
}))(Chat)
App.js
import React,{ Component} from 'react';
import * as AWS from 'aws-sdk';
import {
Platform,
StyleSheet,
Text,
View
} from 'react-native';
import gql from 'graphql-tag';
import { graphql,compose} from 'react-apollo';
import generateStupidName from 'sillyname';
import localStorage from 'react-native-sync-localstorage';
import Chat from './Chat';
import { Async } from 'react-async-await';
import createPerson from './querys/createPerson';
const CHAT_USER_NAME_KEY = 'CHAT_USER_NAME'
const CHAT_USER_ID_KEY = 'CHAT_USER_ID'
class App extends Component {
async componentDidMount() {
let name = localStorage.getItem(CHAT_USER_NAME_KEY)
if (!name) {
name = generateStupidName()
const result = await this.props.createPersonMutation({
variables: { name }
})
localStorage.setItem(CHAT_USER_NAME_KEY, result.data.createPerson.name);
localStorage.setItem(CHAT_USER_ID_KEY, result.data.createPerson.id);
}
}
render() {
const name = localStorage.getItem(CHAT_USER_NAME_KEY)
const userId = localStorage.getItem(CHAT_USER_ID_KEY)
return (
<View style={styles.container}>
<Chat name={name} userId={userId} />
</View>
);
}
}
// const createPerson =gql`
// mutation createPerson($name:String!){
// createPerson(input :{
// name : $name
// }){
// id
// name
// }
// }
// `
// export default graphql(createPerson,{name:'createPersonMutation'})(App)
export default compose(
graphql(createPerson, {name:'createPersonMutation'}))(App)
const styles = StyleSheet.create({
container: {
flex: 1,
justifyContent: 'center',
alignItems: 'center',
backgroundColor: '#F5FCFF',
},
welcome: {
fontSize: 20,
textAlign: 'center',
margin: 10,
},
instructions: {
textAlign: 'center',
color: '#333333',
marginBottom: 5,
},
});
Iam not understanding this error Please help me.Thanks! in Advance
Please check the format of
this.props.listMessagesQuery.listMessages
As the error defined that particular data or props you are passing are not an object.
console.log(this.props.listMessagesQuery.listMessages)
check if you find it in current formate. If you don't find anything share you this console.log result. Hope it helps you
you are not sending listMessagesQuery.listMessages as a props to Chat.js component you are only sending name and userId as props to Chat component
your existing code in App.js
<Chat name={name} userId={userId} />
you need to send
<Chat name={name} userId={userId} listMessagesQuery={}/>