Why is prevState argument null in calculateState method in Flux? - reactjs

I set state in constructor this.state = {};, but prevState argument in calculateState method is null. Where should I set initial state of container?
class QuestionnairesContainer extends Component {
static getStores() {
return [QuestionnairesStore];
}
static calculateState(prevState) {
return {
questionnairesList: QuestionnairesStore.getState().questionnairesList,
pagingObject: prevState.pagingObject
};
}
constructor(props) {
super(props);
this.state = {
pagingObject: someData
};
}
render() {
return (
<section>
</section>
);
}
}
export default Container.create(QuestionnairesContainer);

I have found a solution on flux GitHub.
static calculateState(prevState) {
const init = prevState ? {} : {
pagingObject: someData,
};
return {
...init,
questionnairesList: QuestionnairesStore.getState().questionnairesList
};
}
constructor(props) {
super(props);
}

Related

How to receive props only after state of parent has updated?

I'm trying to build a little weather widget, where the geolocation of the user is captured in one component and then passed onto a child component which fetches the weather data (based on the location) and then eventually renders an icon indicating the current weather conditions.
I'm passing the longitude and latitude state as props to my WeatherWidget. Unfortunately, the WeatherWidget also receives the initial state null. How I can I avoid that?
Thank you for your help!
class GetGeolocation extends Component{
constructor(){
super();
this.state = {
lngt: null,
latd: null
}
}
componentDidMount(){
this.getLocation()
}
getLocation = () => {
if(navigator.geolocation){
navigator.geolocation.getCurrentPosition(position => {
this.setState({lngt: position.coords.longitude.toFixed(4)});
this.setState({latd:position.coords.latitude.toFixed(4)});
}
);
};
}
render(){
return (
<>
<WeatherWidget lngt = {this.state.lngt} latd = {this.state.latd} />
</>
)
}
class WeatherWidget extends Component{
constructor(props){
super(props);
this.state = {
weather:[]
}
}
componentWillReceiveProps(nextProps){
this.getWeather(nextProps)
}
getWeather = (location) => {
console.log(location)
// The console logs twice:
// First:
//{lngt: "-12.3456", latd: null}
//Then, the correct values:
//{lngt: "-12.3456", latd: "78,9999"}
}
Don't use componentWillReceiveProps, that will be deprecated in later versions of React.
But also, you can just setup conditional logic in your life-cycle methods to determine what code to execute.
componentWillReceiveProps(nextProps){
//condition says if both value are truthy then run code.
if(nextProps.lngt && nextProps.latd){
this.getWeather(nextProps)
}
}
You can also use componentDidUpdate()
componentDidUpdate(){
//condition says if both value are truthy then run code.
if(this.props.lngt && this.props.latd){
this.getWeather(this.props)
}
}
One option is to conditionally render in the parent component:
class GetGeolocation extends React.Component {
constructor(props) {
super(props);
this.state = {
lngt: null,
latd: null
};
}
componentDidMount() {
this.getLocation();
}
getLocation = () => {
// Simulate the network request
setTimeout(() => this.setState({ lngt: 100 }), 1000);
setTimeout(() => this.setState({ latd: 100 }), 1000);
};
render() {
const { lngt, latd } = this.state;
if (!lngt || !latd) return null;
return <WeatherWidget lngt={lngt} latd={latd} />;
}
}
class WeatherWidget extends React.Component {
constructor(props) {
super(props);
this.state = {
weather: []
};
}
componentDidMount() {
this.getWeather(this.props);
}
getWeather = location => {
console.log(location);
};
render() {
return null;
}
}

React: fire render after promise completed

Please note, that I a fetching data from AWS DynamoDB.
...
class Test extends Component {
constructor(props) {
super(props);
this.state = {
contactList: []
}
}
componentDidMount() {
var getItemsPromise = db.scan({ TableName: "tester" }).promise();
getItemsPromise.then((data) => this.setState({ contactList: data.Items }));
}
render() {
return (
<div>{this.state.contactList[0].link.S}</div>
);
}
}
export default Test;
I am trying to render the returned value, but can't. If I set
render() {
console.log(this.state.contactList[0].link.S);
return (
<div>test</div>
);
}
it works. Why is that? Why is it not working when I set it straight inline?
this.state.contactList[0] is undefined before the promise is resolved, so this.state.contactList[0].link will give rise to an error.
You could e.g. return null from the render method until the array has been filled with your objects:
class Test extends Component {
// ...
render() {
if (this.state.contactList.length === 0) {
return null;
}
return <div>{this.state.contactList[0].link.S}</div>;
}
}

Access another component's method and have defaultProps for it

I'd like to access another component's userLogout function.
I have read this react-js-access-to-component-methods. However, the only way that seems to work for me is what follows.
Does anyone know another way that would be easier, shorter? My goal is to get all the logic out of Base component.
#azium pointed out that I'm using a derived class. The goal was initially to have access to static defaultProps so the problem was approached the wrong way.
class Funcs extends React.Component {
// this is the derived class way I was hoping to have (much cleaner)
static defaultProps = {
text: 'hello'
}
constructor(props) {
super(props);
}
userLogout() {
console.log('userLogout');
}
render() {
return null;
}
}
class Base extends React.Component {
constructor(props) {
super(props);
this.state = {};
}
MyWidget = (el, refCb) => {
ReactDOM.render(<Funcs ref={refCb} />, el);
};
componentDidMount() {
this.MyWidget(document.getElementById('nothing'), widget => {
console.log('there you are...', widget);
this.setState({
widget
});
// works too
this.widget = widget
});
}
render() {
console.log('widget', this.state.widget, this.widget);
return <div id="nothing" />
}
}
Here's the solution to have defaultProps on a non derived class.
class Funcs {
constructor(props) {
this.props = Object.assign({}, this.defaultProps, props);
}
defaultProps = {
userLogout: {
onCompleted: () => this.props.nextRouter.pushRoute('home_v01', { lng: this.props.nextRouter.query.lng }),
onError: err => console.log('An error occured, ', err)
}
};
userLogout = () => {
LogoutMutation.commit({
environment: this.props.environment,
onCompleted: this.props.userLogout.onCompleted,
onError: this.props.userLogout.onError
});
};
}
class Base extends React.Component {
constructor(props) {
super(props);
this.state = {};
}
funcs = new Funcs({
environment: this.props.relay.environment,
nextRouter: this.props.nextRouter,
userLogout: {
onCompleted: () => console.log('LOG OUT')
}
});
render() {
return <div onClick={this.funcs.userLogout}>Log out</div>
}
}

How to make this available inside render function

This is my React component:
constructor(props) {
super(props);
this.state = {
};
this.showChart = this.showChart.bind(this)
}
showChart() {
console.log('test')
}
render() {
{this.showChart} //throws error that, this is undefined
return () (
{this.showChart} //prints test
)
}
Now, if I want to call the function from render() but outside return() what should I do?
Your Component syntax is incorrect at a few places. this is available inside render.
constructor(props) {
super(props);
this.state = {
};
this.showChart = this.showChart.bind(this)
}
showChart() {
console.log('test')
}
render() {
this.showChart()
return (
<div>{this.showChart()}</div>
)
}
EDIT:
You can also work with arrow functions to bind said functions to your component. By doing this, you don't have to bind every function. It looks a lot cleaner:
constructor(props) {
super(props);
this.state = {
};
}
showChart = () => {
console.log('test')
}
render() {
this.showChart()
return (
<div>{this.showChart()}</div>
)
}
replace {this.showChart} with this.showChart()inside the render function. So your new code should be
render(){
this.showChart();
return(
{this.showChart}
);
}

React context passed but not updated after setState

I manage to pass context through children but only once. Context is never updated.
Yet I have seen many examples working like that, including react docs: https://facebook.github.io/react/docs/context.html
Here is my code:
Parent Component:
class App extends React.Component {
constructor(props) {
super(props);
this.state = {
window:{
height:null,
width:null
}
};
}
getChildContext() {
return {
window: this.state.window
}
}
componentDidMount () {
window.addEventListener('resize', this.handleResize.bind(this));
this.handleResize();
}
componentWillUnmount () {
window.removeEventListener('resize', this.handleResize.bind(this));
}
handleResize (){
this.setState({
window:{
width:window.innerWidth
|| document.documentElement.clientWidth
|| document.body.clientWidth,
height:window.innerHeight
|| document.documentElement.clientHeight
|| document.body.clientHeight
}
});
}
render() {
console.log(this.state.window);
// --> working
return (
{this.props.children}
);
}
}
App.propTypes = {
children: React.PropTypes.node.isRequired
};
App.childContextTypes = {
window: React.PropTypes.object
}
export default App;
Child Component:
class Child extends React.Component {
constructor(props, context) {
super(props);
this.state = {};
}
render () {
console.log(this.context.window);
// --> passed on first render, but never updated
return (
...
)
}
}
Child.contextTypes = {
window: React.PropTypes.object.isRequired
};
export default Child
Am i missing something?

Resources