React: How do I pass props to state component? - javascript

Need help passing state as a prop to another state component. I'm very new to React and I'm not sure what I'm doing wrong. When I console.log inside the Timer component it displays undefined but when I console.log in the Main component it displays the object perfectly.
class Main extends React.Component {
constructor() {
super()
this.state = {
isLoaded: false,
itemsP:{}
}
}
componentDidMount() {
fetch("https://api.spacexdata.com/v3/launches/next")
.then(response => response.json())
.then(
(resData) =>
this.setState({
isLoaded: true,
itemsP: resData
})
)
}
render() {
console.log(this.state.itemsP) //this will console.log the object from the api
return (
<main>
<Timer nextLaunch={this.state.itemsP} />
</main>
)
}
}
//Timer component
class Timer extends React.Component{
constructor(props) {
super(props)
this.state = {
nextDate: props.nextLaunch.launch_date_utc
}
}
render() {
console.log(this.state.nextDate) //will console log UNDEFINED why is this?
return (
<div>
//display something....
</div>
)
}
}
here is the link for the API that I'm using for reference.

#tlrmacl might have answered it there. It's missing the this keyword. In theory, you might be assigning the initial value still to the state.
It is due to how react lifecycle works
On your componentDidMount(), you are calling setState after the jsx gets mounted to the DOM. this.state.itemsP is given an initial value of {} then after the mount, it will receive its new value from comopnentDidMount()
Inside your Timer component, you are assigning the first value of this.props.nextLaunch to a new state. It doesn't have the chance to update the value. Instead of doing this:
this.state = {
nextDate: props.nextLaunch.launch_date_utc
}
use props.nextLaunch.launch_date_utc directly:
console.log(props.nextLaunch.launch_date_utc)
For more information check out this tweet by Dan Abramov here

From ur parent component u pass value as nextLaunch
Dont forget to call props in ur parent component constructor
constructor(props) {
super(props);
this.state = {
isLoaded: false,
itemsP: 'myValue'
}
}
<Timer nextLaunch={this.state.itemsP} />
So in your Timer Component to print ur value you have to call ur props this way this.props.nextLaunch
class Timer extends React.Component {
render() {
return <p>My nextLaunch data are {this.props.nextLaunch}.</p>;
}
}
Hope it helps

//Timer component
class Timer extends React.Component{
constructor(props) {
super(props)
this.state = {
nextDate: this.props.nextLaunch.launch_date_utc
}
}
You need this.props instead of props in your constructor

Related

React setState doesn't update

I saw thousands post about it so I am a bit confuse, I did use arrow function, I binded my method changed for componentDidUpdate but I still can't manage to
in my async call setState my data and pass it to my child component.
ParentComponent
class ParentComponent extends React.Component {
constructor(props) {
super(props);
this.state = {
activeNav: 1,
loading: true,
data: []
};
this.fetchData = this.fetchData.bind(this);
}
fetchData = () => {
var that = this;
getMyData()
.then(res => {
console.log(res); // Output res Object
that.setState({
data: res
})
})
.catch((error) => {
console.log(error);
});
}
componentDidUpdate = () => this.fetchData()
render() {
const { data, loading } = this.state;
return (
<>
<ChildComponent data={this.data} loading={loading}/>
</>
);
}
}
ChildComponent
class CurrentUp extends React.Component {
constructor(props) {
super(props);
}
componentDidUpdate = () => {
console.log(this.props.data); // Output []
console.log(this.props.loading); // Output true
}
}
render() {
console.log(this.props.data); // Output []
console.log(this.props.loading); // Output true
return (
<div>
</div>
);
}
}
What am I missing ?
Solved, i am not sure how. I kept trying different stuff
You are console logging the data in the componentDidMount method. This method runs only once when a component is mounted. When you update the data in the parent component, and pass it to the child component, it doesn't re-create the entire component instance. Only the updates are passed down. You can access those updates in componentDidUpdate method. Or, if you are directly accessing props, you can log the data inside the render method.
class CurrentUp extends React.Component {
constructor(props) {
super(props);
}
render = () => {
console.log(this.props.data);
console.log(this.props.loading);
return null;
}
}
Other points:
You don't need to bind the function with this if you are using an arrow function.
Inside the arrow function, it is safe to use the this keyword. No need of assigning it to that.
Try to bring some more code cause I see the child component you posted is an instead current up component. Send the real child component and then move the var in your fetchData function to the constructor.

React app: props do not get updated externally

I have a React app that gets initialized as simple as:
let globalTodos = some_fetch_from_localstorage();
...
function changeGlobalTodos() {
globalTodos = another_fetch_from_localstorage();
}
...
ReactDOM.render(<ReactApp todos={globalTodos} />, document.getElementById('app'));
Inside of the app I'm doing the following:
constructor(props) {
super(props);
this.state = {
todos: []
};
}
static getDerivedStateFromProps(nextProps, prevState) {
if (nextProps.todos !== prevState.todos) {
return { todos: nextProps.todos };
} else return null;
}
componentDidUpdate(prevProps, prevState) {
if (prevProps.todos !== this.props.todos) {
this.setState({ todos: this.props.todos });
}
}
The problem is that whenever I update globalTodos, the props on the React app don't get updated: it stays on the initial globalTodos's value.
I have tried playing with getDerivedStateFromProps is being called only on first setup of the props while componentDidUpdate never gets called :-/
What am I missing here?
I can't leave a comment, so I'll just post this here. React won't re-render unless you're updating a state.
I'd make globalTodos a state and add onto it from there using setState, then you can pass that on as a prop to the child component in your case ReactApp. You don't need to change them as states in your child component.
Example:
class Parent extends Component {
constructor(props) {
super(props);
this.state = {
globalTodos: initialFetchedArray
};
}
changeGlobalTodos() {
let newTodos = fetchNewArray;
this.setState({globalTodos: newTodos});
}
ReactDOM.render(<ReactApp todos={globalTodos} />, document.getElementById('app'));
}
//You just need your prop here, here you can do whatever you want to do with the array if it's display you can use map
class Child extends Component {
render {
return(
{this.props.todos}
)
}
}
Really the main thing here is making your globalTodos a state, using setState to change that state, and just passing that state down as a prop.

prop passed to component return undefined

I have asynchronous method get called and I need the value from it to be render on the first cycle so it will be passed to the next component that being render and I cant make id done. The component is rendered before the value is returned which cause the the prop be undefined when it passed.
Any idea how can I delay the rendering till the value get returned?
CODE SAMPLE:
export default class App extends Component {
constructor(props) {
super(props);
this.state = {
valueHere:''
}
}
componentWillMount() {
axios.post(url, {
username:this.state.username
})
.then((res) => {
this.setState({
valueHere:res.data
})
})
}
render() {
return(
<AnotherComponent
someValue={this.state.valueHere}
/>
)
}
}
export default class AnotherComponent extends Component {
constructor(props) {
super(props);
}
render() {
console.log(this.props.someValue) // undefined
return(
<View/>
)
}
}
You can use conditional rendering.
So basically, you check if that value exists then render the component else return null/any other component;
render() {
return( this.props.someValue? <ActualComponeent /> : <div> some loader...</div>)
}

Get default state value by receiving prop data - React

I'm new to react.js.
I'd like to get default value of state following_status by receiving props.user.following_status.
I'm passing user object ( user = { following_status: 'following', id:123 } ) to ReactionButton component. ReactionButton component is looks like this:
class RelationButton extends React.Component {
constructor(props){
super(props);
console.log(props.user.following_status) # undefined!!!
this.state = {
following_status: props.user.following_status
}
...
render() {
if (this.state.following_status == 'following') {
<UnFollowBtn/>
} else {
<FollowBtn/>
}
}
RelationButton was called by UserCardHeader component.
const UserCardHeader = (props) => {
const user = props.user;
return(
<header className="user-card--full__header">
<RelationButton user={user}></RelationButton>
</header>
)
}
I don't understand why console.log(props.user.following_status) returns undefined. I googled many websites like those:
React component initialize state from props
accessing props inside react constructor
those answers suggest
class FirstComponent extends React.Component {
constructor(props) {
super(props);
this.state = {
x: props.initialX
};
}
}
but this didn't work for me.
If I add componentWillReceiveProps to the codes above,
componentWillReceiveProps(props){
console.log(props.user.following_status) #=> "following"
this.setState({following_status: props.user.following_status})
}
everything works well. However I think it's weird solution and sometimes doesn't work. Why can't I receive object props in constructor(props) {} section?
Without the full code, we can't tell what's wrong but it is obvious that following_status comes asynchronously to the component and that's why is not accessible right away in the constructor.
To somehow fix it you can detect if props have changed and reset state accordingly in componentDidUpdate.
class RelationButton extends React.Component {
constructor(props){
super(props);
console.log(props.user.following_status) # undefined!!!
this.state = {
following_status: props.user.following_status
}
}
componentDidUpdate(prevProps) {
if(prevProps.user.following_status !== this.props.user.following_status) {
this.setState({ following_status: this.props.user.following_status })
}
}
render() {
// you forgot about return statements :
if (this.state.following_status == 'following') {
return <UnFollowBtn/>
} else {
return <FollowBtn/>
}
}
}

Rendering component with its state

I'm really confused now about lifecycle hooks. Here's my code:
App.js:
class App extends Component {
constructor(props){
super(props);
this.state = {
arrayOfComponents: []
}
}
componentDidMount(){
//i get the properties from the server which responds with the database's elements
fetch('http://localhost:3001/')
.then(res => res.json())
.then(arrayOfData => this.setState({arrayOfComponents: arrayOfData}))
.catch(err => console.log(err))
}
render() {
console.log(this.state) //first returns empty array, after the mount returns the db's array of elements
return (
<div className="App">
<Component name='First' id={1} componentsComponents={this.state.arrayOfComponents} />
</div>
);
}
}
Component.js:
class Component extends React.Component{
constructor(props){
super(props);
this.state={
componentsComponents: []
}
}
//here i was tried with componentDidMount, componentWillMount to set the
//this.props.componentsComponents to this.state.componentsComponents
//but it didn't work
renderComponents = () => {
if(this.state.componentsComponents.length){
return this.state.componentsComponents.filter(c => c.inhertedFromId === this.props.id).map(c => {
return <Component name={c.name} id={c.id} componentsComponents={this.props.componentsComponents} />
})
}
}
render(){
return(
<div>
{this.renderComponents()}
</div>
)
}
}
So what i want to do is to the components renders themselves, depending on the array they get from the App.js. But how to set the state before the render happens? Can i somehow ask the component to render again if it did mount? Or any other solutions?
You can simply assign this.props.componentsComponents in constructor itself only.
constructor(props){
super(props);
this.state={
componentsComponents: this.props.componentsComponents||[]
}
}
Bring Filter Up To App
Here it appears you are not calling renderComponents, and you are also trying to render a Component inside itself, which is difficult to reason about. Bring the renderComponents function up to App, and render the data using Component inside of App, and simply pass props down to a stateless Component, which may be a simpler solution to represent the data.
If a recursive call is indeed the best way to represent this data, may need to use getDerivedStateFromProps to move props into state on update, if you wish to store that data in state - something like:
static getDerivedStateFromProps(nextProps) {
return {
componentsComponents: nextProps.componentsComponents
}
}
Added to Component.

Categories

Resources