如何将反应钩子内的所有状态变量传递给我的子组件
How do I pass all state variables inside react hooks to my child component
我正在尝试使用挂钩获取功能组件中的所有状态。相当于...this.state
。我避免将状态单独传递给 Context.Provider
.
因为 this.state
在函数中不可用。 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 }
将组件中的所有状态传递给提供程序中的值的最佳方法是什么。
<RecipeContext.Provider value={}>
{props.children}
</RecipeContext.Provider>
你可以这样使用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
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>
)
}
这可以通过使用 useReducer 进一步改进! :)
您已经有很多状态。不要像使用 类.
中的 setState
函数那样使用 useState
一个建议,如果您不想像使用 类 中的 setState 一样使用 useState,请对变量使用相同的 "labels" 并尝试,如果可以的话, 有一个状态。
// 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: '',
});
(代码少,易于维护)
关于避免通过上下文提供程序传递状态;这不是您必须的选择。否则,没有理由使用它。
我会做的是保留您的其余代码并更改最后几行代码。有这样的东西:
(顺便说一句,您的 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;
当然这只是一个例子。您也可以像有人说的那样使用 useReducer
。这样你就可以像对待 Redux 一样对待你的本地状态。
现在您有两个选项,具体取决于您使用的是 Stateful 还是 Stateless 组件。对于 Stateful 组件:使用以下方式访问提供者的上下文(值):
<RecipeContext.Consumer>
{value => (
<SomeComponent />
)}
</RecipeContext.Consumer>
// OR
class SomeComponent extends Component {
render() {
let value = this.context;
}
}
SomeComponent. contextType = RecipeContext;
对于无状态组件:
const SomeComponent = props => {
const value = useContext(RecipeContext);
};
我在上面解释的内容可以在这里找到:https://es.reactjs.org/docs/hooks-reference.html#usecontext。
同样在 link 中,您将找到如何使用 useReducer
的示例。这在这种情况下会很棒,而不是像我那样传递所有函数,你可以传递一个动作 dispatch
并传递一个 type 作为你想要触发的动作和从中获取新状态。
但是,您必须使用上下文中的值Provider
。
我使用的一种模式是制作单独的状态变量,然后创建一个将它们拉到一起的模式对象。这似乎是多余的,但它使设置状态变得简单(没有疯狂的嵌套展开运算符来设置状态),然后如果您需要在应用程序的其他地方通过名称访问状态变量,您也可以这样做。我真希望有一些简单的方法可以自动使用字符串访问状态变量,但我不知道有一个。
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')
我正在尝试使用挂钩获取功能组件中的所有状态。相当于...this.state
。我避免将状态单独传递给 Context.Provider
.
因为 this.state
在函数中不可用。 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 }
将组件中的所有状态传递给提供程序中的值的最佳方法是什么。
<RecipeContext.Provider value={}>
{props.children}
</RecipeContext.Provider>
你可以这样使用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
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>
)
}
这可以通过使用 useReducer 进一步改进! :)
您已经有很多状态。不要像使用 类.
中的setState
函数那样使用 useState
一个建议,如果您不想像使用 类 中的 setState 一样使用 useState,请对变量使用相同的 "labels" 并尝试,如果可以的话, 有一个状态。
// 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: '',
});
(代码少,易于维护)
关于避免通过上下文提供程序传递状态;这不是您必须的选择。否则,没有理由使用它。
我会做的是保留您的其余代码并更改最后几行代码。有这样的东西:
(顺便说一句,您的 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;
当然这只是一个例子。您也可以像有人说的那样使用 useReducer
。这样你就可以像对待 Redux 一样对待你的本地状态。
现在您有两个选项,具体取决于您使用的是 Stateful 还是 Stateless 组件。对于 Stateful 组件:使用以下方式访问提供者的上下文(值):
<RecipeContext.Consumer>
{value => (
<SomeComponent />
)}
</RecipeContext.Consumer>
// OR
class SomeComponent extends Component {
render() {
let value = this.context;
}
}
SomeComponent. contextType = RecipeContext;
对于无状态组件:
const SomeComponent = props => {
const value = useContext(RecipeContext);
};
我在上面解释的内容可以在这里找到:https://es.reactjs.org/docs/hooks-reference.html#usecontext。
同样在 link 中,您将找到如何使用 useReducer
的示例。这在这种情况下会很棒,而不是像我那样传递所有函数,你可以传递一个动作 dispatch
并传递一个 type 作为你想要触发的动作和从中获取新状态。
但是,您必须使用上下文中的值Provider
。
我使用的一种模式是制作单独的状态变量,然后创建一个将它们拉到一起的模式对象。这似乎是多余的,但它使设置状态变得简单(没有疯狂的嵌套展开运算符来设置状态),然后如果您需要在应用程序的其他地方通过名称访问状态变量,您也可以这样做。我真希望有一些简单的方法可以自动使用字符串访问状态变量,但我不知道有一个。
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')