I'm trying to pass state from main page to a details page. The details page changes the parent state successfully by calling function (via props). However, a re-render is not triggered and the details page is not updated (despite it having updated props correctly). How can I pass state to a child page (details) such that it will trigger it to re-render?
It seems like a similar question is asked here
but I don't quite understand it. I'm not using redux as this is a small app.
//Navigator Setup
const AppNavigator = createStackNavigator({
Home: {
screen: Home}
Details: {
screen: Details}
);
//Home page:
this.props.navigation.navigate("Details", {
changeDetails: this.changeDetails
details: this.state.details})
}
//Details page
let details = props.navigation.getParam("details", "n/a");
let changeDetails = props.navigation.getParam("changeDetails", "n/a");
//change things
//render stuff
Passing the data with navigation.navigate won't trigger a re-render since it is passing a copy of the props. You will need to find an other way to send the props so that your component gets aware of the changes.
You can for example connect your component to your redux state (assuming you're using redux). If you are not using redux, you can send a refresh fucntion from your parent component navigate('Child', { refresh: refreshFunction } to the Child
You can then access this function in the child ( via something like this.props.navigation.state.params.refresh();or const refresh() = this.props.navigation.getParam('regresh') ) before the back action. This will trigger updating the parent state to update.
I hope this help you
You have pass the data in correct way but while extracting the data you went wrong
let details = props.navigation.getParam("details", "n/a");
just put this.props
like this
console.log(props.navigation.getParam.changeDetails);
console.log(props.navigation.getParam.details)
console.log is just for debugging purpose you can assign it to any variable And you will receive the data in object form or in json form.set this in constructor, render() or where you want to use this data
as document say:
this.props.navigation.navigate('YOUR TARGET NAME', {
itemId: 86,
otherParam: 'anything you want here',
});
here is the full example,good luck
edit :
in child component in componentDidUpdate() method you must check that if props are changed set new props to state,then component will be re-rendered with new state's:
componentDidUpdate(){
if ('YOUR DATA FROM PARENT' != 'PREVIOUS DATA FROM PARENT SAVED IN STATE'){
this.setState({yourState: 'YOUR DATA FROM PARENT'})
}
Related
I'm new to React and creating small app with REST Api to get some practice! So, when I clicking to pokeBox, in sider must be displaying details information about selected pokemon.
I attaching screenshot , how it looks now.
But I have no idea how I have to get constant with Pokemon.url.
It's my structure:
Wrapper (Parrent Component);
1.1. PokemonsList (Children);
1.1.1 GridCards (Children of PokemonsList)
1.2. About (Children)
So, I must get pokemon.url from GridCards and how I understanding, save in parrent component.
But, how is it was with functional component?
Using the hooks you can set a state in the parent component:
const [selectedPokemon, setSelectedPokemon] = useState()
and trigger a function when a new pokeBox is clicked in which to call setSelectedPokemon(pokemon.url).
In this way, you just have to pass selectedPokemon to the child component About in which you can render what you want based on the selectedPokemon value.
I suggest you put a piece of state in the parent component that you can pass to the about component (here you will save the data of the pokemon you want to display). Then you make a function to set data to the state you've just created and pass it to the PokemonList component so you can call that function when you click the name of the pokemon.
In wrapper:
this.state = {
selectedPokemon: {} // Pass this state to about comp as props
}
// Pass this function to pokemonList comp as props
setPokemon = (pokemon) => this.setState({ selectedPokemon: pokemon });
I have an action that sends a Firebase query that relies on store data - specifically the uid.
The uid IS populated and stored upon user sign-in, which is stored in an auth object.
In this component, I'm retrieving the uid, firing the action, receiving the payload, updating the state with a reducer, and rendering the component with the data.
I've mapped the state to props thus (initially this.props.uid is always undefined):
function mapStateToProps(state, props) {
return {
uid: state.auth.uid,
componentData: state.thisComponent.componentData
}
}
I check the uid thus (written before render()):
componentWillReceiveProps(nextProps) {
if (nextProps.uid !== this.props.uid) {
this.props.actions.getComponentData(nextProps.uid)
}
}
The action and query work, but only if the component is refreshed - not if the component is simply navigated to. Additionally, I'm using redux-persist to hydrate the store and persist its state:
**index.js**
... combineReducers etc
let store = createStore(rootReducer, applyMiddleware(thunk), autoRehydrate());
persistStore(store)
How do I make sure that componentWillReceiveProps always dispatches the action and the payload is rendered - whether the page is opened from a different link or when it is refreshed?
componentWillReceiveProps is not run when the component is mounted, it is only run, like you say, after initialization and when the component will receive a new set of props.
I believe you are looking for componentWillMount, anything put in here will run before the component is mounted, meaning, this life-cycle event is invoked immediately before the render method.
componentWillMount(props) {
props.actions.getComponentData(props.uid)
}
Take a look at http://busypeoples.github.io/post/react-component-lifecycle/ for more information
Depending on your use case you might need it on both.
Is it adviceable to change the props to load my page with fresh data.
I have a requirement where I need to load the HTML page from JSON, example given below
formSchema:{
"label": "Step 1",
"action": "Next",
"Fields":[
{
"type":"text",
"label":"First Name",
"name":"fname"
"value":"Abraham"
}
]
}
I'm able to load the HTML page from the above JSON schema, but when user clicks the submit button there will a new schema loaded through ajax call. Now if I pass the new schema to my reducers can I directly replace the props with the newSchema, as I heard mutating the props is not adviceable. In such case how do I update my props only with new data.
If I do it as follows in my reducer
return Object.assign(...formSchema, action.formSchema);
I see in console the next state is shown as object instead of an array even though I load the same set of data.
You can use componentWillReceiveProps() method in React to update your view with new props.
componentWillReceiveProps() is invoked before a mounted component receives new
props. If you need to update the state in response to prop changes (for example, to reset it), you may compare this.props and nextProps and perform state transitions using this.setState() in this method.
Note that React may call this method even if the props have not changed, so make sure to compare the current and next values if you only want to handle changes. This may occur when the parent component causes your component to re-render.
React doesn't call componentWillReceiveProps with initial props during mounting. It only calls this method if some of component's props may update. Calling this.setState generally doesn't trigger componentWillReceiveProps.
It looks like this:
class YourComponent extends Component{
constructor(props) {
this.state = {
message: props.message // initial prop
}
}
componentWillReceiveProps(nextProps){
this.setState({message: nextProps.message} // updated prop
}
render() {
return (
<div><h1>{this.state.message}</h1></div>
)
}
}
Note: In this code you're not updating/mutating props, instead you're using new props (nextProps) and updating the state of your view.
I'm probably missing something very obvious and would like to clear myself.
Here's my understanding.
In a naive react component, we have states & props. Updating state with setState re-renders the entire component. props are mostly read only and updating them doesn't make sense.
In a react component that subscribes to a redux store, via something like store.subscribe(render), it obviously re-renders for every time store is updated.
react-redux has a helper connect() that injects part of the state tree (that is of interest to the component) and actionCreators as props to the component, usually via something like
const TodoListComponent = connect(
mapStateToProps,
mapDispatchToProps
)(TodoList)
But with the understanding that a setState is essential for the TodoListComponent to react to redux state tree change(re-render), I can't find any state or setState related code in the TodoList component file. It reads something like this:
const TodoList = ({ todos, onTodoClick }) => (
<ul>
{todos.map(todo =>
<Todo
key={todo.id}
{...todo}
onClick={() => onTodoClick(todo.id)}
/>
)}
</ul>
)
Can someone point me in the right direction as to what I am missing?
P.S I'm following the todo list example bundled with the redux package.
The connect function generates a wrapper component that subscribes to the store. When an action is dispatched, the wrapper component's callback is notified. It then runs your mapState function, and shallow-compares the result object from this time vs the result object from last time (so if you were to rewrite a redux store field with its same value, it would not trigger a re-render). If the results are different, then it passes the results to your "real" component" as props.
Dan Abramov wrote a great simplified version of connect at (connect.js) that illustrates the basic idea, although it doesn't show any of the optimization work. I also have links to a number of articles on Redux performance that discuss some related ideas.
update
React-Redux v6.0.0 made some major internal changes to how connected components receive their data from the store.
As part of that, I wrote a post that explains how the connect API and its internals work, and how they've changed over time:
Idiomatic Redux: The History and Implementation of React-Redux
My answer is a little out of left field. It sheds light on a problem that led me to this post. In my case it seemed the app was Not re-rendering, even though it received new props.
React devs had an answer to this often asked question something to the tune that if the (store) was mutated, 99% of the time that's the reason react won't re-render.
Yet nothing about the other 1%. Mutation was not the case here.
TLDR;
componentWillReceiveProps is how the state can be kept synced with the new props.
Edge Case: Once state updates, then the app does re-render !
It turn out that if your app is using only state to display its elements, props can update, but state won't, so no re-render.
I had state that was dependent on props received from redux store. The data I needed wasn't in the store yet, so I fetched it from componentDidMount, as is proper. I got the props back, when my reducer updated store, because my component is connected via mapStateToProps. But the page didn't render, and state was still full of empty strings.
An example of this is say a user loaded an "edit post" page from a saved url. You have access to the postId from the url, but the info isn't in store yet, so you fetch it. The items on your page are controlled components - so all the data you're displaying is in state.
Using redux, the data was fetched, store was updated, and the component is connected, but the app didn't reflect the changes. On closer look, props were received, but app didn't update. state didn't update.
Well, props will update and propagate, but state won't.
You need to specifically tell state to update.
You can't do this in render(), and componentDidMount already finished it's cycles.
componentWillReceiveProps is where you update state properties that depend on a changed prop value.
Example Usage:
componentWillReceiveProps(nextProps){
if (this.props.post.category !== nextProps.post.category){
this.setState({
title: nextProps.post.title,
body: nextProps.post.body,
category: nextProps.post.category,
})
}
}
I must give a shout out to this article that enlightened me on the solution that dozens of other posts, blogs, and repos failed to mention. Anyone else who has had trouble finding an answer to this evidently obscure problem, Here it is:
ReactJs component lifecycle methods — A deep dive
componentWillReceiveProps is where you'll update state to keep in sync with props updates.
Once state updates, then fields depending on state do re-render !
This answer is a summary of Brian Vaughn's article entitled You Probably Don't Need Derived State (June 07, 2018).
Deriving state from props is an anti-pattern in all its forms. Including using the older componentWillReceiveProps and the newer getDerivedStateFromProps.
Instead of deriving state from props, consider the following solutions.
Two best practice recommendations
Recommendation 1. Fully controlled component
function EmailInput(props) {
return <input onChange={props.onChange} value={props.email} />;
}
Recommendation 2. Fully uncontrolled component with a key
// parent class
class EmailInput extends Component {
state = { email: this.props.defaultEmail };
handleChange = event => {
this.setState({ email: event.target.value });
};
render() {
return <input onChange={this.handleChange} value={this.state.email} />;
}
}
// child instance
<EmailInput
defaultEmail={this.props.user.email}
key={this.props.user.id}
/>
Two alternatives if, for whatever reason, the recommendations don't work for your situation.
Alternative 1: Reset uncontrolled component with an ID prop
class EmailInput extends Component {
state = {
email: this.props.defaultEmail,
prevPropsUserID: this.props.userID
};
static getDerivedStateFromProps(props, state) {
// Any time the current user changes,
// Reset any parts of state that are tied to that user.
// In this simple example, that's just the email.
if (props.userID !== state.prevPropsUserID) {
return {
prevPropsUserID: props.userID,
email: props.defaultEmail
};
}
return null;
}
// ...
}
Alternative 2: Reset uncontrolled component with an instance method
class EmailInput extends Component {
state = {
email: this.props.defaultEmail
};
resetEmailForNewUser(newEmail) {
this.setState({ email: newEmail });
}
// ...
}
As I know only thing redux does, on change of store's state is calling componentWillRecieveProps if your component was dependent on mutated state and then you should force your component to update
it is like this
1-store State change-2-call(componentWillRecieveProps(()=>{3-component state change}))
I am using react and redux for an web application I am building.
For the most part everyhthing is working just fine however I have one major issue when it comes to passing props from the parent.
I have a main component which connects to redux and obtains my store. I pass the props just fine:
{ this.props.children && React.cloneElement(
this.props.children,
{
preset: this.state.preset,
children: this.state.babies,
child: this.state.currentChild,
name: this.state.firstName,
}
)}
So my particular page gets this props. I then pass needed props to child components, however I cannot access the props on mount or any other lifecycle method that react provides. The only place they seem to be available is in the render method, and thats after running a undefined check:
let child = this.props.child;
if(child.birthdate != undefined) {
// do stuff here
}
It looks like i receive undefined twice then props finally come in and I am able to work with them.
My question is what lifecycle component should i be accessing to format my data. I have ran all available methods in the docs to try to console.log and see where I am at but they all return empty. The only place I actually get the props in in the render.
My explanation is poor i know but any help would be appreciated.
-E
componentWillReceiveProps lifecycle method should do a trick. You can get a prop that is incoming and format it there. After that you can set formatted data in component state and use it in your render method like this.
componentWillReceiveProps(nextProps) {
this.setState({
formattedBirthdate: nextProps.child.birthdate
});
}
The second option will be to do the same in your constructor.
After that you can output your formattedBirthdate in render, like this:
this.state.formattedBirthdate && <div>{this.state.formattedBirthdate}</div>