2
votes

<App foo="123" />

@connect((state) => state, () => {})
class App extends Component

And I'd like to render App with 123.
But, if state in MapStateToProps has a foo key and its value is abc, the component will render abc.

I could check ownProps.

@connect((state, ownProps) => ({...state, ...ownProps}), () => {})
class App extends Component

and merge ownProps and state. But If I start dispatching actions to update foo in Redux, state will always be abc. ownProps will always override keys in state.

I could dispatch an action when the component mounts.

componentDidMount() {
  dispatchFoo(this.props.value)
}

when component mounts, I'm dispatching the value @connect((state) => state, () => {})`

Store will be updated with abc, the value of the own props. Redux will update and the component will render once more.
But this time, state will be abc in ..

@connect((state) => state, () => {})

What is the best way to set something like this up? Preferably that doesn't require the component to render twice (I'm using SSR).

In my case, I'm using NextJS and making an API call to fetch data in getInitialProps. Return of getInitialProps puts data on props. Those props are given to App. When user changes state, the App needs data from state now, not props

2
Why would you pass the same prop key to the component in two different places/in two different ways? (aka connect and inline). That sounds like a really error prone way to write code.John Ruddell
I'm not. I'm passing App the prop foo with value 123. After which, user action change the value of foo via redux. Connect maps the store state to props. And state has the foo key.GN.
Yes, exactly what im describing. Why would your state in redux have a key that is identical to an inline prop? You should have separate prop keys for what you want to send inline from what is in the connected storeJohn Ruddell
I'm using NextJS and making an API call to fetch data in getInitialProps. Return of getInitialProps puts data on props. Those props are given to App. When user changes state, the App needs data from state now, not props. Can you elaborate on how to use this separate prop?GN.
Don't pass foo as a prop on the component, let connect pass that prop. If you need a different value there initially, then set the store up with that initial valueJohn Ruddell

2 Answers

0
votes

You have 2 options:

1. use defaultProps

defaultProps can be defined as a property on the component class itself, to set the default props for the class. This is used for undefined props, but not for null props. For example:

App.defaultProps = {
    foo: true
};

See defaultProps from React's blog.

2. setup an initial state

Inside your reducer you can set your state initial values, that values will be available via mapStateToProps:

const initialState = {
    foo: false
};

export default function(state = initialState, action) {
    console.log('action', action);
    switch (action.type) {
        case types.SOME_ACTION:
            return {
                ...state,
            foo: true
            };
        case types.ANOTHER_ACTION:
            return {
                ...state,
                foo: false
            };
        default:
            return state;
    }
}

In general, I don't see any point of overriding the same props inside mapStateToProps as it should prevent your app from being updated by redux.

0
votes

if i am not wrong you want to implement something known as uncontrolled components. If so I will suggest you to implement it in following way.


Class Page extends React.Component{
  static getInitialProps(){
    return {foo:"Some Value"}
  }
  render(){
    return <Provider store={createStore(reducer,{foo:this.props.foo})}>
      <App/>
    </Provider>

  }
}

Then your app.js will be

@connect(mapStateToProps,{dispatchFoo})
Class App extends React.Component{
 componentDidMount(){
   this.props.dispatchFoo({foo:"some new value"});
 }
 render(){
   <div>{this.props.foo}</div>
 }

}