我为按钮切换创建了一个示例。
这是通过useContext
(存储数据)和useReducer
(处理数据)完成的。而且工作正常。
这是CodeSandBox Link的工作方式。
version 1
只是在单击按钮时发送。
然后,我创建了version 2
的切换。基本上只是将分派放在一个自定义的挂钩中。但不知何故,它不起作用。
// context
export const initialState = { status: false }
export const AppContext = createContext({
state: initialState,
dispatch: React.dispatch
})
// reducer
const reducer = (state, action) => {
switch (action.type) {
case 'TOGGLE':
return {
...state,
status: action.payload
}
default:
return state
}
}
//custom hook
const useDispatch = () => {
const {state, dispatch} = useContext(AppContext)
return {
toggle: dispatch({type: 'UPDATE', payload: !state.status})
// I tried to do toggle: () => dispatch(...) as well
}
}
// component to display and interact
const Panel = () => {
const {state, dispatch} = useContext(AppContext)
// use custom hook
const { toggle } = useDispatch()
const handleChange1 = () => dispatch({type: 'TOGGLE', payload: !state.status})
const handleChange2 = toggle // ERROR!!!
// and I tried handleChange2 = () => toggle, or, handleChange2 = () => toggle(), or handleChange2 = toggle()
return (
<div>
<p>{ state.status ? 'On' : 'Off' }</p>
<button onClick={handleChange1}>change version 1</button>
<button onClick={handleChange2}>change version 2</button>
</div>
)
}
// root
export default function App() {
const [state, dispatch] = useReducer(reducer, initialState)
return (
<AppContext.Provider value={{state, dispatch}}>
<div className="App">
<Panel />
</div>
</AppContext.Provider>
);
}
不确定那里发生了什么。但是我认为调度状态出了点问题。
(我尝试过,如果有效负载未处于处理状态(例如某些硬代码的东西),那么它会起作用,因此此时应该触发分派)
有人可以帮我吗?感激!!!
答案 0 :(得分:1)
好吧,React.dispatch
没有这样的东西。其值为undefined
export const AppContext = createContext({
state: initialState,
// useless
// dispatch: undefined
dispatch: React.dispatch
});
// dispatch function won't trigger anything.
const {state, dispatch} = useContext(AppContext);
版本1 实际上是应该使用上下文的方式,尽管通常情况下,您将需要添加一个额外的备注步骤(取决于用例),因为在每个渲染上都分配了一个新对象{ {1}}即使{state,dispatch}
可能相同也总是会导致渲染。
请参阅此类备忘录用例示例。
如果我的观点不清楚,请参见HMR评论:
如果许多组件访问 上下文,然后记忆是一个好主意,当具有 提供者出于改变上下文以外的其他原因而重新渲染。
答案 1 :(得分:1)
您是正确的,切换必须是一个函数,但是您正在分派动作类型UPDATE,而reducer对该动作没有任何作用。
Dennis是正确的,您提供上下文的初始值没有意义,并且最好将其保留为空,因为提供者将提供该值。
丹尼斯(Dennis)的useMemo建议不会优化您的示例,因为当状态更改时App会重新呈现,因此将永远不会使用记忆的值。
这是您的代码的有效示例,其中包含我所做的更改:
@Controller
public class indexController {
// Tomcat Jasper to return jsp files
@RequestMapping("index")
public String getIndex() {
System.out.println("test1");
return "index.jsp";
}
const { createContext, useReducer, useContext } = React;
const initialState = { status: false };
//no point in setting initial context value
const AppContext = createContext();
const reducer = (state, action) => {
switch (action.type) {
case 'TOGGLE':
return {
...state,
status: action.payload,
};
default:
return state;
}
};
const useDispatch = () => {
const { state, dispatch } = useContext(AppContext);
return {
//you were correct here, toggle
// has to be a function
toggle: () =>
dispatch({
//you dispatch UPDATE but reducer
// is not doing anything with that
type: 'TOGGLE',
payload: !state.status,
}),
};
};
const Panel = () => {
const { state, dispatch } = useContext(AppContext);
const { toggle } = useDispatch();
const handleChange1 = () =>
dispatch({ type: 'TOGGLE', payload: !state.status });
const handleChange2 = toggle; // ERROR!!!
return (
<div>
<p>{state.status ? 'On' : 'Off'}</p>
<button onClick={handleChange1}>
change version 1
</button>
<button onClick={handleChange2}>
change version 2
</button>
</div>
);
};
function App() {
const [state, dispatch] = useReducer(
reducer,
initialState
);
return (
<AppContext.Provider value={{ state, dispatch }}>
<div className="App">
<Panel />
</div>
</AppContext.Provider>
);
}
ReactDOM.render(<App />, document.getElementById('root'));