Are we doing the Context vs. Redux debate? No. Just consider this article as someone who hasn’t heard of Redux, someone who’s new to React, managing state that needs to be shared by multiple components, with React’s built-in functionality.
Every component that’s using a particular context re-renders if anything tin that context changes.
Provider will have hooks and redux logic
<Provider>
must be passed a value
prop, this value is what the Consumer will have access to. This will be your values in state and funtcions to modify state etc. 1import React, { createContext, useContext, useState } from 'react'
2
3import { sampleData } from 'sample-data.ts'
4
5const ListsContext = createContext(sampleData.lists)
6ListsContext.displayName = 'ListsContext'
7
8// the hook to use our context and have reducer logic
9function useListsContext() {
10 const context = useContext(ListsContext)
11 if (!context) throw new Error('ListsContext must be used with ListsProvider')
12 return context
13}
14
15// the provider we'll export
16function ListsProvider(props: any) {
17 const [data, setData] = useState(sampleData.lists)
18
19 const value = { data, setData }
20
21 return <ListsContext.Provider value={value} {...props} />
22}
23
24export { useListsContext, ListsProvider }
1import React, { Fragment } from 'react'
2
3import { Text } from 'react-native'
4
5import * as Type from './Lists.types'
6import { useListsContext, ListsProvider } from './ListsContext'
7
8export default function Lists(props: any) {
9 const data: Type.Lists = useListsContext()
10
11 return (
12 <ListsProvider>
13 <Fragment {...props}>
14 <Header>
15 <Text>Lists</Text>
16 </Header>
17 <Body>
18 {data.map((list: Type.List) => (
19 <Item key={list.id}>
20 <Title>{list.title}</Title>
21 <Description>3 items</Description>
22 </Item>
23 ))}
24 </Body>
25 </Fragment>
26 </ListsProvider>
27 )
28}
Leigh Hailday passes {children} https://www.youtube.com/watch?v=u06qAON66iw
Kent C. Dodds does not https://kentcdodds.com/blog/application-state-management-with-react
1import React from 'react'
2
3import { View } from 'react-native'
4import styled from 'styled-components/native'
5
6import { Color } from 'Theme'
7
8interface TagProps {
9 title: string
10 desc: string
11 color: string
12}
13
14interface TagsProps {
15 data: any
16}
17
18export function Tag({ title, desc, color, ...rest }: TagProps) {
19 return (
20 <StyledTag color={color} {...rest}>
21 <Title>{title}</Title>
22 <Description>{desc}</Description>
23 </StyledTag>
24 )
25}
26
27export default function Tags({ data, ...rest }: TagsProps): JSX.Element {
28 return (
29 <Container {...rest}>
30 {data.map((tag) => (
31 <Tag key={tag.id} title={tag.title} desc={tag.description} color={tag.color} />
32 ))}
33 </Container>
34 )
35}
1// TagsContext.tsx
2import React, { createContext, useContext, useState } from 'react'
3
4import { sampleData } from 'sample-data'
5
6const TagsContext = createContext(sampleData.tags)
7TagsContext.displayName = 'TagsContext'
8
9export function useTagsContext() {
10 const context = useContext(TagsContext)
11
12 if (!context) throw new Error('useTagsContext must be used within TagsProvider')
13 return context
14}
15
16export function TagsProvider(props: any) {
17 const [data, setData] = useState(sampleData.tags)
18
19 const value = { data, setData }
20 return <TagsContext.Provider value={value} {...props} />
21}
1// Tags.tsx
2import React from 'react'
3
4import styled from 'styled-components/native'
5
6import { Color } from 'Theme'
7
8import { useTagsContext, TagsProvider } from './TagsContext'
9
10interface TagProps {
11 title: string
12 desc: string
13 color: string
14}
15
16export function Tag({ title, desc, color, ...rest }: TagProps): JSX.Element {
17 return (
18 <StyledTag color={color} {...rest}>
19 <Title>{title}</Title>
20 <Description>{desc}</Description>
21 </StyledTag>
22 )
23}
24
25export function Tags(props: any): JSX.Element {
26 const data = useTagsContext()
27
28 return (
29 <TagsProvider>
30 <Container {...props}>
31 {data.map((tag) => (
32 <Tag key={tag.id} title={tag.title} desc={tag.description} color={tag.color} />
33 ))}
34 </Container>
35 </TagsProvider>
36 )
37}
useReducer
Standalone react-devtools exist, but the Context shows as a component inside the veryyyy long component tree. And you can’t search with Ctrl + F
even if you give your context a displayName
. Have fun finding your context provider and consumer..
You should definitely enable console.log
by running npx react-native log-android
while the app is running. You only have to enable it once.
react-native-debugger for Redux is far superior. react-context-devtool looks good, but that only appears to be working with React for web and not React Native. Same for reactext
No out of the box solution exists. It’d probably be a manual implementation along the lines of saving every change to LocalStorage
(web) or AsyncStorage
(native), and then reading from storage instead of Context if the app is offline
redux-persist
I built an app where a couple of components were using their own contexts. I gave up at the point where i couldn’t debug the actions that i was dispatching and being unable to see a nice diff in state change that was happening as a result..
Switching from Redux to React Context felt like i was going out of my way to make my dev life difficult. Things that just worked with Redux (and Redux Toolkit) like type definitions, devtools are something you have to put in extra manual effort for in React Context.
As of this writing, i still fail to understand why i shouldn’t be using Redux. I hated its verbosity at one point, but Redux Toolkit (which is now the default approach) has made it cool and typescript friendly. Context is not at par in terms of DX.
I would stick with Redux just for the DevTools alone. If i had to switch, i’d probably invest in learning MobX..