4

I'm new in react hooks and I just don't see this on docs:

const MyComponent = ({myProp}) => {
 const [myPropHook, setPropHook] = useState(myProp)
...
}

I'm wondering if this is a good practice?

gpbaculio
  • 5,693
  • 13
  • 60
  • 102
  • In my opinion this is ok if you expect to use `myProp` only as initial value, otherwise I would suggest you use `useRef()` in case you need to update your local state when `myProp` changes. – alex kucksdorf Jul 01 '19 at 16:10
  • There's a sensible use case for this. If for example you have a form and the props hold a server state of an object, you want state to hold what the user has currently typed in and props to only update once the server has updated the object state. – apokryfos Jul 01 '19 at 16:25
  • @apokryfos In that example you should use the props until the internal state has been updated. – Will Jenkins Jul 01 '19 at 16:31
  • @WillJenkins To clarify. The internal state is e.g. a database row on the server. The props hold the row data. The component state will hold user modifications on that data but the props should not be updated until the modified data goes to the server and the server updates the row. In that case not using the state means your form inputs will be read-only. this assumes there will be a submit button that triggers the server update as opposed to an update-as-you-type kind of box – apokryfos Jul 01 '19 at 16:39

4 Answers4

5

The value you pass to useState is used as a starting value for the state variable. So, when your component props change, they will not affect the state variable you are using. The initial value would be the first props sent to the component and after that can be modified only using the setPropHook function.

So, in short, it is definitely a code smell to use props as initializers for useState because reading the code does not correctly convey what will actually happen.

Arsalan Ahmad
  • 648
  • 5
  • 16
3

You don't see it much because it doesn't make a lot of sense in terms of how a React app should distribute its state.

If a prop value is set higher up the tree, it shouldn't be used as part of the separate state within a component. It makes sense to use prop values to determine the state of a component indirectly as in 'if the prop is this, then set the state to that', but not to directly copy the prop in to the initial value.

In other words, the internal state of a component (accessed via the useState and useReducer Hooks) should be determined by the component, not directly by the parent(s).

Will Jenkins
  • 9,507
  • 1
  • 27
  • 46
3

Yes, this is bad. What you're doing is passing a prop to the state, and it is discouraged by many.

The React docs says that "using props to generate state often leads to duplication of “source of truth”, i.e. where the real data is.". The danger is that if the props is changed without the component being refreshed, the new prop value will never be displayed, because the initialization of state from props only runs when the component is first created.

The only exception would be to use the prop as a seed for an internally-controlled state. After several years of react development, I've never encountered such a case.

Further reading:
React component initialize state from props (SO question)
React Anti-Patterns: Props in Initial State (medium.com article)
Why Setting Props as State in React.js is Blasphemy (blog post)

Nino Filiu
  • 16,660
  • 11
  • 54
  • 84
1

If you are trying to receive a prop to that functional component, then yes, but not exactly like you have it written. So in the parent component you will have something like this:

const App = () => {
  const [resource, setResource] = useState("posts");

and then there is a component inside the JSX like so:

const App = () => {
  const [resource, setResource] = useState("posts");

  return (
    <div>
      <div>
        <button onClick={() => setResource("posts")}>Posts</button>
        <button onClick={() => setResource("todos")}>Todos</button>
      </div>
      <ResourceList resource={resource} />
    </div>
  );
};

That ResourceList component has to be able to receive the props that the App component is passing to it. Inside a class-based component you would do {this.props.resource}, but in our case, where its a functional component using React hooks you want to write it like so:

const ResourceList = (props) => {
  const [resources, setResources] = useState([]);

or via ES6 destructuring like so:

const ResourceList = ({ resource }) => {
  const [resources, setResources] = useState([]);
Daniel
  • 14,004
  • 16
  • 96
  • 156
  • I don't see where you are using the props in the child ResourceList. You are just initializing the state to an empty array and don't reference props anywhere in the child component. Did I miss something? – MattoMK Aug 29 '22 at 20:31