[英]How do I pass all state variables inside react hooks to my child component
I am trying to get all the states within the functional components using hooks.我正在尝试使用钩子获取功能组件中的所有状态。 Equivalent to
...this.state
.相当于
...this.state
。 I am avoiding passing the state individually to the Context.Provider
.我避免将状态单独传递给
Context.Provider
。
Since this.state
is not available within the function.由于
this.state
在函数中不可用。 state
is undefined. state
未定义。
import React, { useState, useEffect } from 'react'
const RecipeContext = React.createContext()
const RecipeProvider = (props) => {
const [showHomeButton, setShowHomeButton] = useState(false)
const [recipes, setRecipes] = useState([])
const [loading, setLoading] = useState(true)
const [search, setSearch] = useState('')
const fetchRecipe = async () => {
const recipeData = await fetch(`https://api.myjson.com/bins/t7szj`)
const { recipes } = await recipeData.json()
setRecipes(recipes)
setLoading(false)
}
const handleSubmit = async (e) => {
e.preventDefault()
setLoading(true)
url = `${url}&q=${search}`
fetchRecipe(url)
setShowHomeButton(true)
}
const handleSearchChange = (e) => {
setSearch(e.target.value)
}
const handleReturnHome = () => {
fetchRecipe()
}
useEffect(() => {
fetchRecipe()
}, [])
return (
<RecipeContext.Provider value={}>
{props.children}
</RecipeContext.Provider>
)
}
const RecipeConsumer = RecipeContext.Consumer
export { RecipeProvider, RecipeConsumer }
What's best way to pass all the states within the component to value in the provider.将组件内的所有状态传递给提供程序中的值的最佳方法是什么。
<RecipeContext.Provider value={}>
{props.children}
</RecipeContext.Provider>
Use an object as the state
使用对象作为状态
const RecipeProvider = (props) => {
//Declare an object as the state
const [megaState, setMegaState] = 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()
//UPDATE STATE WITHOUT MUTATING
setMegaState({
...megaState
recipes,
loading: false
})
}
const handleSubmit = async (e) => {
e.preventDefault()
setLoading(true)
url = `${url}&q=${search}`
fetchRecipe(url)
setShowHomeButton(true)
//UPDATE STATE WITHOUT MUTATING
setMegaState({
...megaState
showHomeButton : true
})
}
const handleSearchChange = (e) => {
//UPDATE STATE WITHOUT MUTATING
setMegaState({
...megaState
search : e.target.value
})
}
const handleReturnHome = () => {
fetchRecipe()
}
useEffect(() => {
fetchRecipe()
}, [])
return (
<RecipeContext.Provider value={megaState}>
{props.children}
</RecipeContext.Provider>
)
}
This can be further improved by using useReducer!这可以通过使用 useReducer 进一步改进! :)
:)
You already have many states.你已经有很多状态了。 Don't use
useState
as you were using the setState
function from classes.不要像使用类中的
setState
函数一样使用useState
。
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.一个建议,如果您不想像使用类中的 setState 那样混淆并使用 useState,请为变量使用相同的“标签”,如果可以的话,尝试拥有一个状态。
// 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;关于避免通过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) (顺便说一句,您的
fetchRecipe
函数没有接收参数)
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.你也可以像有人说的那样使用
useReducer
。 This way you could treat your local state like you were working with Redux.这样你就可以像对待 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 .我上面解释的内容可以在这里找到: https : //es.reactjs.org/docs/hooks-reference.html#usecontext 。 Also in the link, you will find an example of how to use
useReducer
.同样在链接中,您将找到有关如何使用
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.在这种情况下这会很棒,而不是像我那样传递所有函数,您可以传递一个单一的动作
dispatch
并传递一个类型作为您想要触发的动作并从中获取新状态。
But, you HAVE TO use the value from the context Provider
.但是,你必须从上下文中使用的值
Provider
。
You can use reducer this way, and add your context, you can follow this architecture example:您可以通过这种方式使用 reducer,并添加您的上下文,您可以遵循以下架构示例:
const initState = {
is_logged: false,
token: "",
error: { type: "", msg: "" },
form: {
first_name: "",
last_name: "",
password: "",
email: ""
}
}
const reducer = (state, action) => {
const { payload } = action
switch (action.type) {
case "form_first_name":
return { ...state, form: { ...state.form, first_name: payload } }
case "form_last_name":
return { ...state, form: { ...state.form, last_name: payload } }
case "form_email":
return { ...state, form: { ...state.form, email: payload } }
case "form_password":
return { ...state, form: { ...state.form, password: payload } }
case "error":
return { ...state, error: payload }
case "success":
return {
...state,
token: payload,
error: { type: "", msg: "" },
is_logged: true
}
default:
throw new Error()
}
}
const AdminClinicContainer = () => {
const [state, dispatch] = useReducer(reducer, initState)
const _register = async () => {
const result = await axios(API_ADMIN_REGISTER)
console.log(result.data)
}
const _login = async () => {
try {
const response = await axios.post(API_ADMIN_LOGIN, {
email: state.form.email,
password: state.form.password
})
console.log(response.data)
dispatch({ type: "success", payload: response.data.token })
} catch (error) {
console.log(error.response.data.error)
dispatch({ type: "error", payload: error.response.data.error })
}
}
const _forgetPsw = async () => {
const result = await axios(API_ADMIN_LOGIN)
console.log(result.data)
}
const _form = (type, payload) => dispatch({ type, payload })
return (
<div>
<AdminClinic
_register={_register}
_login={_login}
_forgetPsw={_forgetPsw}
_form={_form}
state={state}
/>
</div>
)
}
export default AdminClinicContainer
One pattern I have used is to make individual state variables, then create a schema object that pulls them all together.我使用的一种模式是创建单独的状态变量,然后创建一个将它们全部拉在一起的模式对象。 This seems redundant, but it makes setting state simple (no crazy nested spread operators to set state) and then if you need to access your state variables by name somewhere else in your application, you can do that too.
这似乎是多余的,但它使设置状态变得简单(没有疯狂的嵌套扩展运算符来设置状态),然后如果您需要按名称访问应用程序中其他位置的状态变量,您也可以这样做。 I do wish there was some easy way to automatically use a string to access the state variables, but I'm not aware of one.
我确实希望有一些简单的方法可以自动使用字符串来访问状态变量,但我不知道有一种方法。
const [name, setName] = useState('')
const [email, setEmail] = useState('')
// for easy access to all state variables in child components
// using a regular definition within the component means you're
// always referring to the latest version of the state variables
// imagine if you had like 20 state variables to pass...
const schema = {
name: {
state: name,
setState: setName,
},
email: {
state: email,
setState: setEmail,
},
}
// elsewhere, maybe in a child component
schema['name'].setState('Steve')
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.