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.
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);
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}
/>
)
}
//********************************************/
}
I'm managing Todo lists in my app. The main view is a page with all the lists displayed as cards. If you click on one of them, you can modify, update, delete stuff through a modal that appears.
I have a TodoLists reducer that store all the TodoLists. I don't know how to handle the modal. Should I use redux or just local state?
import _ from "lodash";
import React from "react";
import { connect } from "react-redux";
import PropTypes from "prop-types";
import { listsActions } from "../duck";
import NewList from "./NewList";
import Card from "./Card";
import Modal from "./Modal";
class Lists extends React.Component {
constructor(props) {
super(props);
this.state = {
modal: false,
list: {}
};
this.hideModal = this.hideModal.bind(this);
this.renderModal = this.renderModal.bind(this);
}
componentDidMount() {
const { fetchByUserId, user } = this.props;
if (user !== undefined) {
fetchByUserId(user.id);
}
}
hideModal() {
this.setState({
modal: false
});
}
renderModal() {
this.setState({
modal: true
});
}
render() {
const { items } = this.props;
const { modal, list } = this.state;
return (
<div>
<NewProject />
<div className="columns">
{_.map(items, (l) => (
<div
key={l.id}
className="column"
>
<Card
list={l}
onClick={() => this.renderModal(l)}
/>
</div>
))}
</div>
<Modal
className={modal ? "is-active" : ""}
list={list}
onClose={this.hideModal}
/>
</div>
);
}
}
const mapStateToProps = (state) => {
const { user } = state.authentication;
const { items, loading, error } = state.lists;
return {
user,
items,
loading,
error
};
};
export default connect(
mapStateToProps,
{ fetchByUserId: listsActions.fetchByUserId }
)(Projects);
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 have created action and reducers for my application. I am trying to create a new todo and want to save it in state using redux.
action/index.js
let taskID = 0;
export const addTodo = text => {
return { type: "ADD_TODO", text, id: taskID++ };
};
reducers/todos.js
const todo = (state = {}, action) => {
switch (action.type) {
case "ADD_TODO":
return {
id: action.id,
text: action.text,
status: false
};
default:
return state;
}
};
export default todo;
reducers/index.js
import { combineReducers } from "redux";
import todos from "./todos";
const todoApp = combineReducers({ todo });
export default todoApp;
index.js
import React from "react";
import ReactDOM from "react-dom";
import App from "./App";
import registerServiceWorker from "./registerServiceWorker";
import "./index.css";
import { Provider } from "react-redux";
import { createStore } from "redux";
import todoApp from "./reducers/todos";
let store = createStore(todoApp);
ReactDOM.render(
<Provider store={store}><App /></Provider>,
document.getElementById("root")
);
registerServiceWorker();
App.js
import React, { Component } from "react";
import logo from "./logo.svg";
import "./App.css";
import AppBar from "material-ui/AppBar";
import FloatingActionButton from "material-ui/FloatingActionButton";
import MuiThemeProvider from "material-ui/styles/MuiThemeProvider";
import * as strings from "./Strings";
import * as colors from "./Colors";
import styles from "./Styles";
import ContentAdd from "material-ui/svg-icons/content/add";
import Dialog from "material-ui/Dialog";
import FlatButton from "material-ui/FlatButton";
import * as injectTapEventPlugin from "react-tap-event-plugin";
import TextField from "material-ui/TextField";
import { List, ListItem } from "material-ui/List";
import { connect } from "react";
import { addTodo } from "./actions/index";
const AppBarTest = () =>
<AppBar
title={strings.app_name}
iconClassNameRight="muidocs-icon-navigation-expand-more"
style={{ backgroundColor: colors.blue_color }}
/>;
class App extends Component {
constructor(props) {
injectTapEventPlugin();
super(props);
this.state = {
open: false,
todos: [],
notetext: ""
};
this.handleChange = this.handleChange.bind(this);
}
handleOpen = () => {
this.setState({ open: true });
};
handleClose = () => {
this.setState({ open: false });
};
handleCreateNote = () => {
let todos = [...this.state.todos];
todos.push({
id: todos.length,
text: this.state.notetext,
completed: false
});
this.setState({ todos: todos }, () => {
// setState is async, so this is callback
});
this.handleClose();
};
handleChange(event) {
this.setState({ [event.target.name]: event.target.value });
}
_renderTodos() {
return this.state.todos.map(event => {
return (
<ListItem
primaryText={event.text}
key={event.id}
style={{ width: "100%", textAlign: "center" }}
onTouchTap={this._handleListItemClick.bind(this, event)}
/>
);
});
}
_handleListItemClick(item) {
console.log(item);
}
render() {
return (
<MuiThemeProvider>
<div>
<AppBarTest />
<FloatingActionButton
style={styles.fab}
backgroundColor={colors.blue_color}
onTouchTap={this.handleOpen}
>
<ContentAdd />
</FloatingActionButton>
<Dialog
open={this.state.open}
onRequestClose={this.handleClose}
title={strings.dialog_create_note_title}
>
<TextField
name="notetext"
hintText="Note"
style={{ width: "48%", float: "left", height: 48 }}
defaultValue={this.state.noteVal}
onChange={this.handleChange}
onKeyPress={ev => {
if (ev.key === "Enter") {
this.handleCreateNote();
ev.preventDefault();
}
}}
/>
<div
style={{
width: "4%",
height: "1",
float: "left",
visibility: "hidden"
}}
/>
<FlatButton
label={strings.create_note}
style={{ width: "48%", height: 48, float: "left" }}
onTouchTap={this.handleCreateNote}
/>
</Dialog>
<List style={{ margin: 8 }}>
{this._renderTodos()}
</List>
</div>
</MuiThemeProvider>
);
}
}
export default App;
I want to save new todo inside handleCreateNote function, I am not sure how to use store, dispatch here to save it in state. Can anyone help me ?
Change
export default App;
To
function mapStateToProps(state) {
return {
todo: todo
}
}
export default connect(mapStateToProps, actions)(App)
You should also import all the actions using
import * as actions from './action/index';
After all these modify your this function as follows:-
handleCreateNote = () => {
let todos = [...this.state.todos];
let newTodo = {
id: todos.length,
text: this.state.notetext,
completed: false
};
todos.push(newTodo);
this.setState({ todos: todos }, () => {
// setState is async, so this is callback
});
this.props.addTodo(this.state.notetext);
this.handleClose();
};
Also your logic for adding todos is incorrect.
So your action creator should be something like this
let taskID = 0;
export const addTodo = text => {
return {
type: "ADD_TODO",
text: text,
id: taskId++
};
};
Now the reducer also needs to change, so that should be something like this:-
const todo = (state = [], action) => {
switch (action.type) {
case "ADD_TODO":
let newTodo = {
id: action.id,
text: action.text,
status: false
};
return [...state, newTodo]
default:
return state;
}
};
export default todo;
I hope this helps.Not the best of implementations, but will solve your issue.