Reputation: 1209
Given a component hierarchy like this:
<TodoList>
<Todo>
<TodoHeader/>
<TodoBody>
<TodoDetails>
<TodoStatus />
<TodoDetails>
<TodoDescription />
<TodoBody>
<Todo>
</TodoList>
...and a store like this:
{
todos: [
{ id: 1, status: "INCOMPLETE", header: "title one", description: "do a something" },
{ id: 2, status: "INCOMPLETE", header: "title two", description: "something else" },
{ id: 3, status: "COMPLETE", header: "title three", description: "one more thing" },
]
}
Is there a good way for the nested TodoStatus
component to connect to the store without having to pass the id
down
the component hierarchy as props? For example, Todo
could set currentTodoId = 1
as context, which would be available
for child reducers, but are there alternatives to that? Maybe a way for the parent component to reduce the store down to
a single todo that child components would then be able to see...?
At this point, you're probably wanting to ask "why"? Well, consider that there may be several levels of strictly presentational components in between the TodoList
(which is operating on the array of todos) and the nested TodoStatus
(which only wants to operate on a single todo). Having to pass the todoId
down through a hierarchy like this is pretty painful:
<TodoList>
<Todo todoId={1}>
<SomeAnimation todoId={1}>
<SomeLayout todoId={1}>
<SomeOtherAnimation todoId={1}>
<SomeDebugContainer todoId={1}>
<TodoHeader todoId={1}>
<TodoBody todoId={1}>
<TodoDetails todoId={1}>
<TodoStatus todoId={1}> // yay!
At this point, I'm imagining that this is specifically what React context is good for, so there's probably not a Redux-specific pattern, but I'd like to be wrong!
Upvotes: 3
Views: 890
Reputation: 268333
Why do they all need to accept id
as an argument?
Usually some component higher in the hierarchy (e.g. Todo
) would accept either id
or todo
, but the components below would be more specific in what they accept, e.g.
function Todo({ todo }) {
return (
<SomeAnimation>
<SomeLayout>
<SomeOtherAnimation>
<SomeDebugContainer>
<TodoHeader title={todo.title} />
<TodoBody {...todo} />
</SomeDebugContainer>
</SomeOtherAnimation>
</SomeLayout>
</SomeAnimation>
)
}
In this example, TodoHeader
just receives a title
prop directly. If it needs more props, you can spread over todo
properties with {...todo}
like I do in <TodoBody>
. It is not obvious from your example why components like SomeAnimation
would need to know the todo ID either—presumably passing some visual properties of it would be enough.
Similarly, inner components like TodoBody
might pass some of their props down, but again, this doesn’t have to be the ID:
function TodoBody({ title, text, status }) {
return (
<div>
<TodoDetails text={text} />
<TodoStatus status={status} />
</div>
)
}
In general, deep trees returned from render()
usually mean your component structure is suboptimal. You don’t have to use this.props.children
in every component—feel free to keep components in control over their own rendering, and only pass what is necessary to them. Sometimes passing an id
is convenient, other times passing data directly makes dependencies more explicit.
Upvotes: 1
Reputation: 640
Connect seems to be a bad idea, as you said each Todo is a presentation component and should ignore anything about redux store or application.
I have the same problem on a project, for the moment I think it seems painful but it make each element reusable and simplify the code (not the quantity of code but the logic). May be you can simplify your dom/component structure, using react children encapsulation to limit the number of "levels"
Not sure I helped a lot. If you find a great solution I will glad to read it.
Good luck
Upvotes: 0