Local vs Global State: Search Without Redux
Not everything belongs in Redux. The task search feature is a perfect example: filter the Redux state locally with useState and never dispatch a search action.
The task list reads from Redux and dispatches updates. The app is fully wired. Now add search, and make a deliberate decision about where the search state lives.
The answer: in useState, not Redux.
The Rule: Global Only When Shared
Redux exists for state that multiple components need to read or update. If state is needed in exactly one component, local useState is correct.
The search query is typed in Tasks, filtered in Tasks, and displayed in Tasks. No other component cares about it. It never needs to leave the component.
Dispatching a SET_SEARCH_QUERY action to the store would add an action type, a reducer case, a useSelector call, and a useDispatch call, for no benefit. The complexity is real, the gain is zero.
Implementing Local Search
Add a search state property in Tasks.js:
const [search, setSearch] = useState('');Bind it to the search input:
<div className="search-box">
<i className="fas fa-search" />
<input
type="text"
placeholder="Search tasks..."
value={search}
onChange={(e) => setSearch(e.target.value)}
/>
</div>Then derive filteredTasks from the Redux state and the search string:
const tasks = useSelector((state) => state.tasks);
const filteredTasks = tasks.filter((task) =>
task.taskTitle.toLowerCase().includes(search.toLowerCase())
);Render from filteredTasks, not from tasks:
{filteredTasks.map((task) => (
<div key={task.id} className="task">
{/* ... */}
</div>
))}Typing "design" shows only tasks whose title contains "design", case-insensitively. Clearing the input shows all tasks again.
No dispatch. No action type. No reducer change. The Redux state from the store is read once via useSelector, then filtered locally. The store never knows the user is searching.
ExpandSearch filters Redux state locally without touching the store
Why This Matters
This pattern is not obvious. The instinct when using Redux is to put everything through the store. That instinct leads to overly complex applications where trivial UI interactions generate action logs in DevTools, and reducers grow with cases that will never need to be shared.
A useful mental test before creating a Redux state slice: "Will another component ever need to read or update this value?" If the answer is no, keep it in useState.
Redux state: the task list, user authentication, shopping cart, notification queue. Local state: form inputs, modal visibility, search queries, tooltip hover state.
The Essentials
- Search state belongs in
useState. The search query is used in one component. Dispatching a Redux action for it adds complexity with no gain. - Derive filtered data locally.
tasks.filter(...)runs inside the component, reads fromuseSelector, and never touches the store. - The test: before adding something to Redux, ask whether any other component needs it. If not,
useStateis correct.
Further Reading and Watching
- Thinking in React: the official guide on identifying local vs shared state
- Redux FAQ: When should I put state in Redux?: the official answer from the Redux team
Practice what you just read.
Keep reading