React is a popular library for creating web apps and mobile apps.
In this article, we’ll look at some tips for writing better React apps.
Update React Context from Inside a Child Component
We can update the React context by first creating a context, and wrapping the child component in the context consumer.
Then we can use the function returned by the useContext
hook in the child component to do the update.
For instance,e we can write:
const NameContext = React.createContext({
name: "",
setName: () => {}
});
const NameSwitcher = () => {
const { name, setName } = useContext(NameContext);
return (
<button onClick={() => setName("mary")}>
change name
</button>
);
};
const App = () => {
const [name, setName] = useState("");
return (
<NameContext.Provider value={{ name, setName }}>
<h2>Name: {name}</h2>
<div>
<NameSwitcher />
</div>
</NameContext.Provider>
);
};
First, we create the NameContext
with the React.createContext
method.
Then, we have the App
component, which has the name
state and setName
to update the name
state.
We then have the NameContext.Provider
wrapped around all the component that we want to have access to NameContext
.
The nesting can be at any level.
Everything in the value
prop will be returned by useContext
when we use it.
Therefore, in NameSwitcher
, we can use the setName
function to set the name
state, which is also returned by useContext
.
In class components, we can do the same thing.
For instance, we can write:
const NameContext = React.createContext({
name: "",
setName: () => {}
});
class NameSwitcher extends Component {
render() {
return (
<NameContext.Consumer>
{({ name, setName }) => (
<button onClick={() => setName("mary")}>
change name
</button>
)}
</NameContext.Consumer>
);
}
}
class App extends Component {
constructor(props) {
super(props);
this.state = {
language: "en",
setLanguage: this.setLanguage
};
this.setName = this.setName.bind(this);
}
setName(name) {
this.setState({ name });
};
render() {
return (
<NameContext.Provider value={{
name: this.state.name,
setName: this.setName
}}>
<h2>Name: {name}</h2>
<div>
<NameSwitcher />
</div>
</NameContext.Provider>
);
}
}
We have the same context object, but the state change functions, and the states are at different places.
We pass them into the value
prop in on the object.
Since we passed them into the value
prop’s object, we can access them in NameSwitcher
.
The only difference is that we need to add a NameContext.Consumer
and a callback inside it to access the properties.
They’re in the parameter of the callback.
Difference Between useCallback and useMemo
We use useMemo
to memoize a calculation result a function’s calls and renders.
useCallback
is for memoizing a callback itself between renders.
The reference is cached.
useRef
is to keep data between renders.
useState
does the same.
Therefore, we can use useMemo
to avoid heavy calculations.
useCallback
fixes performance issues where inline handlers cause a PureComponent
‘s child to render again.
This happens because function expressions are different referentially each time they render.
Make Ajax Request in Redux
We can make Ajax requests with Redux if we use the Redux Thunk middleware.
To install it, we run:
npm install redux-thunk
To use it, we write:
import { createStore, applyMiddleware } from 'redux';
import thunk from 'redux-thunk';
import rootReducer from './reducers/index';
const store = createStore(rootReducer, applyMiddleware(thunk));
We import the rootReducer
and the thunk
middleware.
And we call applyMiddleware
to apply the thunk
middleware.
Now we can create our own thunk by writing:
const fetchUsers = () => {
return dispatch => {
fetch(`https://randomuser.me/api/`)
.then(res => res.json())
.then(res => {
dispatch(saveUserData(res))
})
}
}
store
.dispatch(fetchUsers())
.then(() => {
//...
})
We pass a function that has the dispatch
parameter, which is the Redux dispatch
function, into the store.dispatch
method.
The dispatch
parameter is called to put the data into the Redux store.
Then to map this function to our component’s props, we can write:
function mapDispatchToProps(dispatch) {
return {
fetchUsers: () => dispatch(fetchUsers()),
};
}
Now we can call this.prop.fetchUsers
to call dispatch our async action.
fetchUsers
is an action creator function, but it’s async.
It can be so thanks to the Redux Thunk plugin.
Conclusion
We can use the context API to share data with class and function components.
If we want a more flexible way to share data, we can use Redux with Redux Thunk.
This lets us set data in an async manner.