I’m working on application using react & redux
and I need setProps but it’s deprecated.
Take a look the error below:
Warning: setProps(…) and replaceProps(…) are deprecated. Instead,
call render again at the top level.Uncaught Invariant Violation: setProps(…): You called
setProps
on
a component with a parent. This is an anti-pattern since props will
get reactively updated when rendered. Instead, change the owner’s
render
method to pass the correct value as props to the component
where it is created.
So how can I set the props? basically is to manage some tabs, take a look below my code:
export default React.createClass({
render: function() {
return (
<div className="demo-tabs">
<Tabs activeTab={this.props.activeTab}
onChange={(tabId) => this.setProps({ activeTab: tabId })} ripple>
<Tab>Stack</Tab>
<Tab>GitHub</Tab>
<Tab>Twitter</Tab>
</Tabs>
<section>
<div className="content">Content for the tab: {this.props.activeTab}</div>
</section>
</div>
);
}
});
Container
import React from 'react';
import TimeLine from './timeline';
import { connect } from 'react-redux';
import { getStackoverflow } from 'api/timeline';
const TimeLineContainer = React.createClass({
componentWillMount: function() {
getStackoverflow();
},
render: function() {
return (
<TimeLine {...this.props} />
)
}
});
const stateToProps = function(state) {
return {
timeline: state.timelineReducer.timeline
}
}
const dispatchToProps = function(dispatch) {
return {
onClick: () => {console.log('timeline was clicked')}
}
}
export default connect(stateToProps, dispatchToProps)(TimeLineContainer)
Reducer
var timelineInitialState = {
github: [],
gist: [],
stackoverflow: [],
twitter: [],
activeTab: 2
}
export default function(state = timelineInitialState, action) {
switch (action.type) {
case 'GET_GITHUB':
//TODO: implement.
break;
case 'GET_GIST':
//TODO: implement.
break;
case 'GET_STACKOVERFLOW':
var stackoverflowState = Object.assign({}, state)
stackoverflowState.stackoverflow = action.stackoverflow;
return stackoverflowState;
break;
case 'GET_TWITTER':
//TODO: implement.
break;
default:
return state;
}
}
2
Answers
Provide onTabChange callback to Timeline component from Container.
Container:
Timeline component:
Presentational components (like your Timeline component) usually shouldn’t manage application state. All data and callbacks they must receive by props.
I suggest you to read article about Presentational and Container components:
https://medium.com/@dan_abramov/smart-and-dumb-components-7ca2f9a7c7d0#.sijqpzk93
It looks like you dived into using Redux without first getting a firm grip of React. I wouldn’t recommend doing this because you appear to be somewhat confused now.
Thinking in React is a great guide and I recommend you to go through it first and get comfortable with the idea of state ownership in React, before using Redux.
In React, things that change over time (“state”) are always “owned” by some component. Sometimes it’s the same component that uses this state for rendering. Sometimes many components need to be synchronized so the state gets “hoisted” to some parent component that can manage them all, and pass that information via props. Whenever state changes, they all get updated.
The important part here is that
props
are meant to be received by parent. If a component wants to change its own props, it is a symptom of a problem:state
for this component, so you can callsetState
onChange
so it can “ask” the value to be changedIn the first case, your code would look like this, and it would be valid React:
However, you can continue the pattern you are already using with
<Tabs>
component inside, and hoist the state even higher. Then your component would need to accept anonChange
prop which it would pass down to<Tabs>
. Now it doesn’t know how the state is updated, and doesn’t hold the state:Neither approach is better or worse, they are used for different purposes. The first one is more convenient when the state is irrelevant to the rest of the app, the second one is convenient when other distant components happen to need it too. Make sure you understand the tradeoffs of both approaches.
Even with the second approach, something’s got to hold the state. It could be another component, or (and this is where Redux comes in) it could be a separate data storage like a Redux store. In this case, rather than supply
onChange
from a parent component, you would useconnect()
to bind anonChange
prop it injects to dispatching a Redux action:And in your reducers, you can handle this action:
In neither case we need
setProps
. You can either:setState
,activeTabId
andonChange
props and manage them from a component higher in the tree that would usesetState
, oractiveTabId
andonChange
as props.