Which is the proper way to use State vs Props - react-native

Trying to figure out which is the way to handle data into react components.
Im actually trying these two ways:
A) The State Way
componentWillReceiveProps(nextProps) {
if (!nextProps.allPostsQuery.loading && !nextProps.allPostsQuery.error) {
this.setState({
postList: nextProps.allPostsQuery.allPosts,
})
}
}
render () {
return (
<FlatList data={this.state.postList}>
)
}
B) The Prop Way
render () {
return (
<FlatList data={this.props.allPostQuery.data}>
)
}
Any suggestions on which/why ?

I imagine that you have implement request from GraphQL because of the apollo-client tag. Hypothesize that if you do not implement Redux, so state way is to make sure the component be refresh because the render method will be triggered. If you already have Redux, so props way is the best way because render method will be trigger when your data change in global state.

you can just use the props way directly. since you're using apollo-client, it is already saved in a redux store. so whenever there's an update in the data from any component, you'll also receive it in this component and thus automatically trigger a re-render.

Related

Re-render React useState without updating the state

I need to re-render this state manually.
const [person] = React.useState(new Person());
I have methods inside the Person class to update it (e.g. person.setName('Tom')).
When I update person using a method from itself, it does not trigger a re-render on the person state.
const carouselData = React.useMemo(() => {
// Doesn't re-render when the fields on the person class update
}, [person]);
Is there a way to force this state to re-render without using a setState function?
Ideally, is there a way to call that re-render from inside the Person class itself?
Or is this totally misusing the useState functionality? Would there be a better React hook to connect this to?
Thanks!
Person could be a prop or in context instead. First create your instance outside of your component.
const person = new Person([]);
Then pass person as a prop.
function App({person}) {
const [personName, setPersonName] = useState(person.personName);
function handleNameChange(txt) {
person.addTodo(txt);
setPersonName(person.personName);
}
function handleSubmit(txt) {
handleNameChange(txt);
}
return (...)
}
you are indeed miss using it, react relies heavy on functional programming. you should be doing something like:
const [person, setPerson] = useState(new Person());
...
setPerson(setPersonName(person, 'Tom'));
that's just a silly example but you get the gist. react will only re-render if state is changed, it won't monitor if the state object is mutating or anything like angularjs used to do

React Hooks and useEffect – best practices and server issues

I am using React Native with functional components. componentDidMount() etc. are not available in functional components, instead I use Hooks. But Hooks don't act like lifecycle methods. I am wondering what the best practices are.
Assumed that we have a function like this one:
const ABCScreen = () => {
const [someHook, setSomeHook] = useState<any>()
useEffect(() => {
// some code inside this function which is called on every component update
}, [])
server.asyncCall().then(data => {
setSomeHook(data)
})
return (<View>
{someHook ? (<Text> `someHook` was assigned </Text>) : (<Text> `someHook` was not assigned, display some ActivityIndicator instead</Text>)}
</View>)
}
Where to place server.asyncCall()? Inside or outside of useEffect?
I think you have a misunderstanding here. The convention is that all the fetching data is going to be placed inside the componentDidMount lifecycle method. React useEffect hook can replace this easily by placing an empty array of dependencies, which means you can place that call inside the useEffect you already have.
Unlike you mention in your code comment, this hook won't be triggered on each component update. It will be only be triggered once the component is being mounted. So, you should be able to do it as follows:
const ABCScreen = () => {
const [someHook, setSomeHook] = useState<any>()
useEffect(() => {
server.asyncCall().then(setSomeHook)
}, [])//only triggered when component is mounted.
In the future, you might want to check the rules of the hooks.

React Functional component is not rendering on props change

I am using react native for development and i am using functional component for development. but i am facing problem in it. As i am getting callback from function and saving it in a state and i want to that component will render again. but component is not rendering again. Please check and provide me detail in
callBackfn = (callback) => {
this.setState({ infomationtosend: callback });
}
this is working fine as if i will print it on console get state correct output. but as it again goes in the ComponentTreat it doesn't render component again. i also tried useEffect but not working. can anyone provide me solution.
<Swiper
from={0}
>
<ComponentTreat
detailsend={this.state.infomationtosend}
appName={"sentText"}
CallBack={(callback) => this.callBackfn(callback)}
/>
</Swiper>
problem in swiper duw to swiper component is not refreshing.
Functional Component state change works like this:
// Inside parent component
const [info, setInfo] = useState(initalValue);
Then to use it in a callback we use closures to access it instead of using this keyword.
function callback(newValue) {
setInfo(newValue);
}
And pass it accordingly to the child props.
Also, checkout useCallback() if you use a lot of callbacks.

React Native (Redux) FlatList jumping to top of list when onEndReached called

I have a FlatList in ReactNative which pulls a list of articles from an API. When the end of the list is reached on scrolling, a further page of articles is pulled from the API and appended to the articles list in a Redux reducer.
The FlatList is set up as:
render() {
return(
<FlatList data={this.props.articles.articles} // The initial articles list via Redux state
renderItem={this.renderArticleListItem} // Just renders the list item.
onEndReached={this.pageArticles.bind(this)} // Simply calls a Redux Action Creator/API
onEndReachedThreshold={0}
keyExtractor={item => item.id.toString()}/>
)};
The Redux 'articles' state object is mapped to the component using the React Redux connect function. The relevant reducer (some items removed for brevity) looks like:
/// Initial 'articles' state object
const INITIAL_STATE = {
articles: [],
loading: false,
error: '',
pageIndex: 0,
pageSize: 10
};
export default(state = INITIAL_STATE, action) => {
switch(action.type){
// The relevant part for returning articles from the API
case ARTICLES_SUCCESS:
return { ...state, loading: false, articles: state.articles.concat(action.payload.items),
pageIndex: action.payload.pageIndex, pageSize: action.payload.pageSize}
default:
return state;
}
}
The payload is a simple object - the articles are contained in the action.payload.items array, and there is additional paging information as well in the payload (not important to this problem).
Everything is fine with the API returning the correct data.
The problem is that when the end of the FlatList is reached on scrolling, the API is called, the new articles are pulled fine and appended to the this.props.articles state object and to the FlatList, BUT the FlatList jumps/scrolls back to the first item in the list.
I think the problem is probably with how I am returning the state in the Redux reducer but I'm not sure why.
Is using concat the correct way to return the new state with the new articles appended?
It might be too late to answer this but I experienced exact same problem having my data served from redux to the component and managed to fix the jumping effect by making my component a normal Component rather than PureComponent allowing me to use shouldComponentUpdate life-cycle hook method. This how the method should look like for you component:
shouldComponentUpdate(nextProps) {
if (this.props.articles === nextProps.articles) {
return false;
}
return true;
}
This method (if implemented) should return a boolean value indicating whether the component should update or not. What i did here was to prevent the component from re-rendering in case the contents of articles hasn't changed.
Hope this would be helpful for you or others who might have faced similar problem.
You need to add to listItem styles height and width. Freezing and jumping maybe because list tried to calculate sizes for all items even if they dont display.
And you can find answer in: https://github.com/facebook/react-native/issues/13727
Using FlatList as component and adding data as props re-renders whole content, so it scrolls to top. I recommend using it directly inside your page and changing its data using this.state, not props

react native route flux re-render component on stack pop

How do you re-run React Component Lifecycle methods to determine if a re-render is necessary when transitioning back to previous screens from stack using React Native Router Flux?
Scenario: Scene A -> B -> A
Lets say, A is the initial scene and we navigate from A to B with a push i.e Actions.SceneB. From B to A with a Pop. When the stack is popped to show Scene-A, none of the React Component methods are called for Scene-A. How do I determine if a re-render is necessary? I searched the Issues listed on the github repo and many seems to have this problem without a solution. How is this problem addressed?
I have the ugly work around of calling Actions.refresh() with a setTimeout.
Work Around that works!
This workaround seems to be hacky. There must be a better way to hook into component lifecycle to determine if a re-render is required. Perhaps a callback into the Component?
<TouchableHighlight underlayColor='#efefef' onPress={() => { Actions.pop(); setTimeout(()=> Actions.refresh(), 500)}} style={styles.cancel}>
Options that do not work
Actions.pop({type: 'reset'})
OR
Actions.refresh({"key" : "sceneA"})
Work Around that works! This workaround seems to be hacky. There must be a better way to hook into component lifecycle to determine if a re-render is required.
<TouchableHighlight underlayColor='#efefef' onPress={() => { Actions.pop(); setTimeout(()=> Actions.refresh(), 500)}} style={styles.cancel}>
This will call shouldComponentUpdate() method which gives an option to determine, if a re-render is required.
I was facing the same issue below code gave me required result.
let count = 0;
moveToMainScreen = () => {
count += 1;
Actions.pop({ refresh: { test: count } });
};
<Button onPress={this.moveToMainScreen}> Click </Button>