You already have many states. Don't use useState as you were using the setState function from classes.
An advice, If you don't wanna get confused and work with useState like you were using the setState from classes, use the same "labels" for the variable and try, if you can, to have one state.
// From this
const [showHomeButton, setShowHomeButton] = useState(false);
const [recipes, setRecipes] = useState([]);
const [loading, setLoading] = useState(true);
const [search, setSearch] = useState('');
// to this - common understanding
const [state, setState] = useState({
showHomeButton: false,
recipes: [],
loading: true,
search: '',
});
(Less code, easy to maintain)
About avoiding to passing the state through the Context Provider; it's not an option you have to. Otherwise, there's no reason to use it.
What I would do, it would be to keep the rest of your code and change the last lines of code. Having something like this:
(btw, your fetchRecipe function is not receiving a parameter)
import React, { useState, useEffect } from 'react'
const RecipeContext = React.createContext()
const RecipeProvider = (props) => {
const [state, setState] = useState({
showHomeButton: false,
recipes: [],
loading: true,
search: '',
});
const fetchRecipe = async () => {
const recipeData = await fetch(`https://api.myjson.com/bins/t7szj`);
const { recipes } = await recipeData.json();
setState({
...state,
recipes,
loading: false,
});
};
const handleSubmit = async (e) => {
e.preventDefault();
fetchRecipe(`${url}&q=${search}`);
setState({
...state,
loading: true,
showHomeButton: true
});
}
const handleSearchChange = (e) => {
e.persist();
setState({
...state,
search: e.target.value
});
};
// this might not needed
const handleReturnHome = () => {
fetchRecipe()
};
useEffect(() => {
fetchRecipe()
}, []);
return (
<RecipeContext.Provider value={{
store: state,
actions: {
fetchRecipe,
handleSearchChange,
handleSubmit,
}
}}>
{props.children}
</RecipeContext.Provider>
)
}
export default RecipeProvider;
Of course this is just and example. You could also make use of useReducer like someone says. This way you could treat your local state like you were working with Redux.
Now you have two options depending if you are using an Stateful or Stateless component. For Stateful component: Get access to the context (value) of your provider using:
<RecipeContext.Consumer>
{value => (
<SomeComponent />
)}
</RecipeContext.Consumer>
// OR
class SomeComponent extends Component {
render() {
let value = this.context;
}
}
SomeComponent. contextType = RecipeContext;
For Stateless components:
const SomeComponent = props => {
const value = useContext(RecipeContext);
};
What I explained above it could be found here: https://es.reactjs.org/docs/hooks-reference.html#usecontext.
Also in the link, you will find an example of how to use useReducer. That would be great in this case, instead of passing all the functions as I did, you could pass one single action dispatch and pass a type as the action you wanna trigger and get a new state from it.
But, you HAVE TO use the value from the context Provider.
[state, setState] = useState(initialState)oruseReducerhooks. I want to stick with that without being verbose.