I wanna add pull to refresh but i dont know what to call from from _Refresh(). I have action, constants and reducers in another page. How can i recall the api.
thanks for help in advance.
I wanna add pull to refresh but i dont know what to call from from _Refresh(). I have action, constants and reducers in another page. How can i recall the api.
thanks for help in advance.
class HomeworkList extends Component {
constructor(props){
super(props);
this.state = {
getHW : null,
refreshing: false,
appState: AppState.currentState,
months : ["Jan","Fev","Mar","Apr","May","Jun","Jul","Aug","Sep","Oct","Nov","Dec"]
}
}
_onRefresh() {
this.setState({refreshing: true});
}
componentDidMount(){
this.props.getHomework();
}
render() {
const {homework,isFetching} = this.props.homework;
if(isFetching){
return(
<View>
<ActivityIndicator
color = '#bc2b78'
size = "large"
/>
</View>
)
}
else{
return (
<ScrollView style={styles.container} refreshControl={
<RefreshControl
refreshing={this.state.refreshing}
onRefresh={this._onRefresh.bind(this)}
/>
}>
<View style={styles.filterView}>
<View style={{flexDirection:'row'}}>
<Icon size={20} name="ios-options" color="#000000" /><Text style={[{color:"#333333"},mainStyles.postTitle]}> FILTER BY</Text>
</View>
<View>
<ScrollView
horizontal={true}
showsHorizontalScrollIndicator={false}
>
{
this.state.months.map((item,i)=>{
return(
<TouchableHighlight key={i} style={styles.filterItem} onPress={() => {}} underlayColor={"#de0000"}><Text >{item}</Text></TouchableHighlight >
)
})
}
</ScrollView>
</View>
</View>
<View style={[styles.titleView,mainStyles.coloredBackground]}>
<TouchableOpacity
>
<Text style={styles.title}>
THIS MONTH
</Text>
</TouchableOpacity>
</View>
<View style={styles.viewPadding}>
{
homework.length ? (
homework.map((item, index) => {
return(
<TouchableOpacity
onPress={() => this.props.navigate('Chat', { title: item })}
key={item.id}
>
<Text style={[styles.listItems,{borderColor:randomHex()}]}>
{item.date}
</Text>
</TouchableOpacity>
)
})
):null
}
</View>
</ScrollView>
);
}
}
}
function mapDispatchToProps(dispatch){
return{
getHomework: () => dispatch(fetchHomeworkFromApi()),
getNaviagationName:()=>dispatch(getNaviagationName())
}
}
function mapStateToProps(state){
return{
homework: state.homework
}
}
export default connect(mapStateToProps,mapDispatchToProps)(HomeworkList);
Dispatch a refresh action inside the refresh callback.
This action should then dispatch any number of other actions to "re fresh" like fetchHomeworkFromApi and getFreshData. Don't re-fetch things that make no sense to refresh.
Inside your reducers make sure that when fresh data arrives, it replaces the old. (Avoid situations where new data is appended to old data)
Very simple, assuming you have API call defined in your redux action, hooked up with redux thunk, something like below
// Action.js
const reduxAction = () => {
return async dispatch => {
dispatch({ type: IS_LOADING });
//Call backend API
const result = axios.xxx();
dispatch({ type: GOT_RESULT, payload: result });
}
}
The above is the very standard API calling from redux, nothing to do with pull to refresh yet.
Now since we have a redux Action for our component, and that you have RefreshControl, you need to update your `_
_onRefresh() {
this.props.reduxAction();
}
As for your RefreshControl, instead of using localState, use the loading state from your redux, something like below
<RefreshControl
refreshing={this.props.refreshing} //Use the one from redux, not component state
onRefresh={this._onRefresh.bind(this)}
/>
Related
How do I get rid of this warning? I know I need to get rid of setState functions in the render method, but I need them, so where should I put them?
export default class List<T> extends React.PureComponent<ListProps<T>> {
state = { wrapped: false, iconName: "arrow-down" };
render(): React.Node {
const { rows, renderRow, title, onPress } = this.props;
if (this.state.wrapped === true) {
list = undefined;
this.setState({ iconName: "arrow-up" });
} else {
list = rows.map((row, index) => (
<View key={index} style={index !== rows.length - 1 ? styles.separator : {}}>
{renderRow(row, index)}
</View>
));
this.setState({ iconName: "arrow-down" });
}
return (
<TouchableWithoutFeedback>
<View style={styles.container}>
<View style={[styles.separator, styles.relative]}>
<Text style={styles.title}>{title}</Text>
<IconButton
style={styles.icon}
onPress={() => this.setState({ wrapped: !this.state.wrapped })}
name={this.state.iconName}
color="black"
/>
</View>
{list}
</View>
</TouchableWithoutFeedback>
);
}}
No, you don't need to get rid of setState calls in your render method in general. You just need to put them so that they are not called in each render call (by binding them to user events like clicks for example) and thereby trigger another re-render, that again calls setState and again re-renders and so on.
So in your particular case, you are firing setState right in the beginning in the if() { ... } else { ... } statements. No matter what this.state.wrapped is, you end up at setState.
Here is a possible solution for how you might want to change your code specifically to make it what I assume you want it to make:
export default class List<T> extends React.PureComponent<ListProps<T>> {
state = { wrapped: false };
render(): React.Node {
const { rows, renderRow, title, onPress } = this.props;
const { wrapped } = this.state;
return (
<TouchableWithoutFeedback>
<View style={styles.container}>
<View style={[styles.separator, styles.relative]}>
<Text style={styles.title}>{title}</Text>
<IconButton
style={styles.icon}
onPress={() => this.setState({ wrapped: !wrapped })}
name={wrapped ? "arrow-up" : "arrow-down"}
color="black"
/>
</View>
{!wrapped && (
<View key={index} style={index !== rows.length - 1 ? styles.separator : {}}>
{renderRow(row, index)}
</View>
)}
</View>
</TouchableWithoutFeedback>
);
}}
Because the value of your icon is directly correlated to wrapped, you don't need to specifically set the icon in the state. Rather infer it from wrapped.
I want to create a Modal component and I want to have possibility to inject to it everything what I want. Firstly I decided to use HOC but then I've changed my solution to render props. Everything works fine but I don't really like this solution. I'm wondering how could I optimize it to make it better. What is the best practise to create such kind of modal where you have button opening this modal beyond modal component. I really don't like that now I have two components with open/close state of modal. And both of them have a toggle method to open/close modal. Any suggestion? Maybe I should stick with the HOC ?
Here's the code with Component.js where CustomModal is used:
toggleModalVisibility = (visible) => {
this.setState({modalVisible: visible});
};
render() {
const question = this.props.questions[this.props.counter];
return (
<View style={styles.questionContainer}>
<CustomModal
visible={this.state.modalVisible}
toggleModalVisibility={this.toggleModalVisibility}
render={() => (
<>
<Text>{question.text}</Text>
<Text>{question.details}</Text>
</>
)}
/>
<View style={styles.questionTextContainer}>
<Text style={styles.questionText}>{question.text}</Text>
<TouchableOpacity onPress={() => this.toggleModalVisibility(!this.state.modalVisible) }>
<FontAwesome5 name='question-circle' size={30} color='#B7DBF3' light />
</TouchableOpacity>
</View>
</View>
)
}
and here's the code of CustomModal.js
export default class CustomModal extends Component {
constructor(props) {
super(props);
this.state = {
isOpen: this.props.visible
};
}
componentDidUpdate(prevProps) {
if (prevProps.visible !== this.props.visible) {
this.setState({isOpen: this.props.visible});
}
}
toggle = (isOpen) => {
this.setState({ isOpen });
this.props.toggleModalVisibility(isOpen)
}
render() {
return (
<View>
<Modal
animationType='slide'
transparent={false}
visible={this.state.isOpen}
>
<View style={{marginTop: 30, marginLeft: 5}}>
<TouchableHighlight
onPress={() => {
this.toggle(!this.state.isOpen)
}}>
<FontAwesome5 name='times-circle' size={30} light />
</TouchableHighlight>
<View>{this.props.render()}</View>
</View>
</Modal>
</View>
)
}
}
I am trying to pass a data (server response) as an argument of a button.Actually in my case there are certain type of workers (listing within cards ). If clicked on a worker it should be saved to db with the corresponding worker's id.Upon clicking on the card of workers there will be a popup showing for confirmation.So if clicked on yes button I'm taking the corresponding worker's id and perform another fetch request for saving it to my db.But this is not working I'm confused how to pass arguments within onclick property of a button and take that argument within fetch method.Following is my code.I'm pasting only a portion of my code below.
updated code
export default class FirstScreen extends Component {
constructor(){
super();
this.state = {
workers: [],
}
}
componentWillMount(){
fetch('http://192.168.1.3:3000/api/worker', {
method:'GET',
headers:{
Accept: 'application/json'
}
})
.then(response => response.json())
.then(responseData =>
this.setState({
workers:responseData
})
)
}
onPressYes = (worker_id) => {
fetch('http://192.168.1.3:3000/api/work_detail',{
method:'POST',
headers:{
Accept:'application/json'
},
body:JSON.stringify({
worker_id
})
})
}
render() {
return (
<View style={{flex:1}}>
<Header />
<ScrollView>
{this.state.workers.map((a, index)=>
<Container>
<CardSection>
<TouchableOpacity
onPress={() => this.popupDialog.show()}
>
<View style={{ maringTop: 10, marginLeft:120}}>
<Image
style={{ height: 100, width: 100 }}
source={{ uri: a.work_type == 'Carpenter' ? images[0].image : images[1].image}}
/>
<Text style={{marginLeft:20, fontSize:20}}>{a.work_type}</Text>
</View>
</TouchableOpacity>
</CardSection>
</Container>
<View style={styles.buttonContainer}>
<TouchableOpacity onPress={() =>console.log('Clicked')}>
<Button
backgroundColor="#FF4500"
title='View Status' />
</TouchableOpacity>
</View>
</ScrollView>
<PopupDialog
ref={popupDialog => {
this.popupDialog = popupDialog;
}}
dialogStyle={{ backgroundColor: "#FFFFFF", height: 180, width:300, borderWidth:1,padding:10}}
overlayBackgroundColor="#fff"
dismissOnTouchOutside={true}
>
<View style={styles.dialogContentView}>
<Text style={{fontSize:18, margingTop:10,color:"#000000"}}>Are you sure you want to submit?</Text>
<View style={{flexDirection:'row'}}>
<View style={styles.button_1}>
<Button
title="Yes"
color="#FF6633"
onPress={() => this.onPressYes(worker_id)}
/>
</View>
<View style={styles.button_1}>
<Button
title="No"
color="#FF6633"
onPress={() =>this._onPressNo() }
/>
</View>
</View>
</View>
</PopupDialog>
</View>
})
);
}
}
workers is the array I'm fetching from server.
Can you try replacing _onPressYes = (a.worker_id) with with _onPressYes = (worker_id) and then
body:JSON.stringify({
worker_id
})
Let me know if that helps.
What I usually do is return a function with the given parameter. I mean wrap a function in a function like the following:
onClickHandler = (value) => () => {
// do whathever you want.
};
<Component onClick={this.onClickHandler(yourValue)}/>
Because the problem in your code is that the function will be execute without calling the onClick event because when you pass the argument to a simple function you are already calling it.
I hope it can help you :)
When i press the <TouchableOpacity> Button, i want the value 'abc' to be appended to the array selectedTags and then <Text> {this.list()} </Text> will print out my array.
But now when i press the Button, nothing display out.
Can anybody know what is the problem with my code?
export default class App extends Component {
constructor(props) {
super(props);
this.state = {
selectedTags: []
}
}
list() {
return this.state.selectedTags.map(function(tags, i){
return(
<View key={i}>
<Text>{tags.name}</Text>
</View>
);
});
}
render() {
return (
<View style={styles.container}>
<TouchableOpacity onPress={() => this.state.selectedTags.push('abc')} key = {1} style={styles.buttonContainer}>
<Text style={styles.buttonText}> Button </Text>
</TouchableOpacity>
<Text> {this.list()} </Text>
</View>
);
}
}
This is because you never call setState, which would trigger re-render of your component.
instead of using:
onPress={() => this.state.selectedTags.push('abc')}
try:
onPress={() => this.setState({selectedTags: this.state.selectedTags.concat('abc')})}
The function list only push data in array but does not rerender the view, todo so you have to use setState or forceUpdate.
You can implement the onpress function like this.
onPress = () =>{
this.state.selectedTags.push("abc");
this.setState({selectedTags : this.state.selectedTags});
}
Is there anyway to redirect user to a different component (also passing arguments with it) without calling render function?
almost in all tutorials i found, they use sameway :
render() {
return (
<NavigatorIOS
style={styles.container}
initialRoute=\{\{
title: 'first',
component: First
}} />
);
}
Now see my code :
in renderRow , for touchablehighlights , i need to be able to redirect user to a page with an argument, (in this case i need to send user to component CourseDetail with argument of course_id, so i can show user course's details)
class Courses extends Component {
constructor(props) {
super(props);
this.state = {
dataSource: new ListView.DataSource({
rowHasChanged: (row1, row2) => row1 !== row2,
}),
loaded: false,
};
}
fetchData() {
// fetching data here
}
componentDidMount() {
this.fetchData();
}
render(){
if (!this.state.loaded) {
return this.renderLoadingView();
}
return (
<View style={{
flex: 1
}}>
<ListView
dataSource={this.state.dataSource}
renderRow={this.renderRow}
style={styles.listView}
/>
</View>
);
}
renderRow(data) {
var header = (
<View>
<View style={styles.rowContainer}>
<View style={styles.textContainer}>
<Text style={styles.title}>{data.nid}</Text>
<Text style={styles.description} numberOfLines={0}>{data.title}</Text>
</View>
</View>
<View style={styles.separator}></View>
</View>
);
///////////
var cid = [];
var content = [];
for(let x=0; x < Object.keys(data.course).length; x++){
cid[x] = data.course[x].course_id;
content.push(
<TouchableHighlight
underlayColor='#e3e0d7'
key={x}
onPress={()=>{console.log(cid[x]);}} //// i need to navigate user to a page with passing arguments (course_id in this case)
style={styles.child}
>
<Text style={styles.child}>
{data.course[x].title}
</Text>
</TouchableHighlight>
);
}
console.log(cid);
var clist = (
<View style={styles.rowContainer}>
{content}
</View>
);
////////////
return (
<Accordion
header={header}
content={clist}
easing="easeOutCubic"
/>
);
}
renderLoadingView() {
return (
<View style={styles.loading}>
<Text style={styles.loading}>
Loading Courses, please wait...
</Text>
</View>
);
}
}
module.exports = Courses;
Thanks in Advance!
By help of this Question here :
React-Native: Cannot access state values or any methods declared from renderRow
I was able to solve the issue by changing :
<ListView
dataSource={this.state.dataSource}
renderRow={this.renderRow}
style={styles.listView}
/>
To :
<ListView
dataSource={this.state.dataSource}
renderRow={this.renderRow.bind(this)}
style={styles.listView}
/>
Hope it will help others who may have the same problem.